mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-07 12:16:44 +01:00
Compare commits
7 commits
5e09f5a4bb
...
cb8fd26922
| Author | SHA1 | Date | |
|---|---|---|---|
| cb8fd26922 | |||
| 4a385b40ff | |||
| fe8ecf8a98 | |||
| 20ccff13de | |||
| 3bb38f2d27 | |||
| 60c0771024 | |||
| ab07fd2b43 |
22 changed files with 718 additions and 747 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -936,9 +936,12 @@ dependencies = [
|
|||
name = "tengri"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"tengri",
|
||||
"tengri_dsl",
|
||||
"tengri_input",
|
||||
"tengri_output",
|
||||
"tengri_proc",
|
||||
"tengri_tui",
|
||||
]
|
||||
|
||||
|
|
|
|||
320
dsl/src/dsl.rs
Normal file
320
dsl/src/dsl.rs
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
use crate::*;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
#[derive(Error, Debug, Copy, Clone, PartialEq)] pub enum ParseError {
|
||||
#[error("parse failed: not implemented")]
|
||||
Unimplemented,
|
||||
#[error("parse failed: empty")]
|
||||
Empty,
|
||||
#[error("parse failed: incomplete")]
|
||||
Incomplete,
|
||||
#[error("parse failed: unexpected character '{0}'")]
|
||||
Unexpected(char),
|
||||
#[error("parse failed: error #{0}")]
|
||||
Code(u8),
|
||||
}
|
||||
|
||||
pub trait TryFromDsl<'state, T>: Sized {
|
||||
fn try_from_expr <'source: 'state> (
|
||||
_state: &'state T, _iter: &mut TokenIter<'source>
|
||||
) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
fn try_from_atom <'source: 'state> (
|
||||
state: &'state T, value: Value<'source>
|
||||
) -> Option<Self> {
|
||||
if let Exp(0, mut iter) = value {
|
||||
return Self::try_from_expr(state, &mut iter)
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Map EDN tokens to parameters of a given type for a given context
|
||||
pub trait Context<'state, U>: Sized {
|
||||
fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state, T: Context<'state, U>, U> Context<'state, U> for &T {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
(*self).get(iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option<T> {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
self.as_ref().map(|s|s.get(iter)).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the const iterator pattern.
|
||||
#[macro_export] macro_rules! const_iter {
|
||||
($(<$l:lifetime>)?|$self:ident: $Struct:ty| => $Item:ty => $expr:expr) => {
|
||||
impl$(<$l>)? Iterator for $Struct {
|
||||
type Item = $Item;
|
||||
fn next (&mut $self) -> Option<$Item> { $expr }
|
||||
}
|
||||
impl$(<$l>)? ConstIntoIter for $Struct {
|
||||
type Kind = IsIteratorKind;
|
||||
type Item = $Item;
|
||||
type IntoIter = Self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a native [Iterator] API over the [ConstIntoIter] [SourceIter]
|
||||
/// [TokenIter::next] returns just the [Token] and mutates `self`,
|
||||
/// instead of returning an updated version of the struct as [SourceIter::next] does.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct TokenIter<'a>(
|
||||
pub SourceIter<'a>
|
||||
);
|
||||
|
||||
impl<'a> TokenIter<'a> {
|
||||
pub const fn new (source: &'a str) -> Self {
|
||||
Self(SourceIter::new(source))
|
||||
}
|
||||
pub const fn peek (&self) -> Option<Token<'a>> {
|
||||
self.0.peek()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TokenIter<'a> {
|
||||
type Item = Token<'a>;
|
||||
fn next (&mut self) -> Option<Token<'a>> {
|
||||
self.0.next().map(|(item, rest)|{self.0 = rest; item})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for TokenIter<'a> {
|
||||
fn from (source: &'a str) -> Self{
|
||||
Self(SourceIter(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<SourceIter<'a>> for TokenIter<'a> {
|
||||
fn from (source: SourceIter<'a>) -> Self{
|
||||
Self(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// Owns a reference to the source text.
|
||||
/// [SourceIter::next] emits subsequent pairs of:
|
||||
/// * a [Token] and
|
||||
/// * the source text remaining
|
||||
/// * [ ] TODO: maybe [SourceIter::next] should wrap the remaining source in `Self` ?
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct SourceIter<'a>(pub &'a str);
|
||||
|
||||
const_iter!(<'a>|self: SourceIter<'a>| => Token<'a> => self.next_mut().map(|(result, _)|result));
|
||||
|
||||
impl<'a> From<&'a str> for SourceIter<'a> {
|
||||
fn from (source: &'a str) -> Self{
|
||||
Self::new(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SourceIter<'a> {
|
||||
pub const fn new (source: &'a str) -> Self {
|
||||
Self(source)
|
||||
}
|
||||
pub const fn chomp (&self, index: usize) -> Self {
|
||||
Self(split_at(self.0, index).1)
|
||||
}
|
||||
pub const fn next (mut self) -> Option<(Token<'a>, Self)> {
|
||||
Self::next_mut(&mut self)
|
||||
}
|
||||
pub const fn peek (&self) -> Option<Token<'a>> {
|
||||
peek_src(self.0)
|
||||
}
|
||||
pub const fn next_mut (&mut self) -> Option<(Token<'a>, Self)> {
|
||||
match self.peek() {
|
||||
Some(token) => Some((token, self.chomp(token.end()))),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Static iteration helper.
|
||||
#[macro_export] macro_rules! iterate {
|
||||
($expr:expr => $arg: pat => $body:expr) => {
|
||||
let mut iter = $expr;
|
||||
while let Some(($arg, next)) = iter.next() {
|
||||
$body;
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn peek_src <'a> (source: &'a str) -> Option<Token<'a>> {
|
||||
let mut token: Token<'a> = Token::new(source, 0, 0, Nil);
|
||||
iterate!(char_indices(source) => (start, c) => token = match token.value() {
|
||||
Err(_) => return Some(token),
|
||||
Nil => match c {
|
||||
' '|'\n'|'\r'|'\t' =>
|
||||
token.grow(),
|
||||
'(' =>
|
||||
Token::new(source, start, 1, Exp(1, TokenIter::new(str_range(source, start, start + 1)))),
|
||||
'"' =>
|
||||
Token::new(source, start, 1, Str(str_range(source, start, start + 1))),
|
||||
':'|'@' =>
|
||||
Token::new(source, start, 1, Sym(str_range(source, start, start + 1))),
|
||||
'/'|'a'..='z' =>
|
||||
Token::new(source, start, 1, Key(str_range(source, start, start + 1))),
|
||||
'0'..='9' =>
|
||||
Token::new(source, start, 1, match to_digit(c) {
|
||||
Ok(c) => Value::Num(c),
|
||||
Result::Err(e) => Value::Err(e)
|
||||
}),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Str(_) => match c {
|
||||
'"' => return Some(token),
|
||||
_ => token.grow_str(),
|
||||
},
|
||||
Num(n) => match c {
|
||||
'0'..='9' => token.grow_num(n, c),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Sym(_) => match c {
|
||||
'a'..='z'|'A'..='Z'|'0'..='9'|'-' => token.grow_sym(),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Key(_) => match c {
|
||||
'a'..='z'|'0'..='9'|'-'|'/' => token.grow_key(),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Exp(depth, _) => match depth {
|
||||
0 => return Some(token.grow_exp()),
|
||||
_ => match c {
|
||||
')' => token.grow_out(),
|
||||
'(' => token.grow_in(),
|
||||
_ => token.grow_exp(),
|
||||
}
|
||||
},
|
||||
});
|
||||
match token.value() {
|
||||
Nil => None,
|
||||
_ => Some(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn to_number (digits: &str) -> Result<usize, ParseError> {
|
||||
let mut value = 0;
|
||||
iterate!(char_indices(digits) => (_, c) => match to_digit(c) {
|
||||
Ok(digit) => value = 10 * value + digit,
|
||||
Result::Err(e) => return Result::Err(e)
|
||||
});
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub const fn to_digit (c: char) -> Result<usize, ParseError> {
|
||||
Ok(match c {
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
|
||||
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
_ => return Result::Err(Unexpected(c))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> {
|
||||
pub source: &'source str,
|
||||
pub start: usize,
|
||||
pub length: usize,
|
||||
pub value: Value<'source>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> {
|
||||
#[default] Nil,
|
||||
Err(ParseError),
|
||||
Num(usize),
|
||||
Sym(&'source str),
|
||||
Key(&'source str),
|
||||
Str(&'source str),
|
||||
Exp(usize, TokenIter<'source>),
|
||||
}
|
||||
|
||||
impl<'source> Token<'source> {
|
||||
pub const fn new (source: &'source str, start: usize, length: usize, value: Value<'source>) -> Self {
|
||||
Self { source, start, length, value }
|
||||
}
|
||||
pub const fn end (&self) -> usize {
|
||||
self.start.saturating_add(self.length)
|
||||
}
|
||||
pub const fn slice (&'source self) -> &'source str {
|
||||
self.slice_source(self.source)
|
||||
//str_range(self.source, self.start, self.end())
|
||||
}
|
||||
pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start, self.end())
|
||||
}
|
||||
pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start.saturating_add(1), self.end())
|
||||
}
|
||||
pub const fn value (&self) -> Value {
|
||||
self.value
|
||||
}
|
||||
pub const fn error (self, error: ParseError) -> Self {
|
||||
Self { value: Value::Err(error), ..self }
|
||||
}
|
||||
pub const fn grow (self) -> Self {
|
||||
Self { length: self.length.saturating_add(1), ..self }
|
||||
}
|
||||
pub const fn grow_num (self, m: usize, c: char) -> Self {
|
||||
match to_digit(c) {
|
||||
Ok(n) => Self { value: Num(10*m+n), ..self.grow() },
|
||||
Result::Err(e) => Self { value: Err(e), ..self.grow() },
|
||||
}
|
||||
}
|
||||
pub const fn grow_key (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Key(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_sym (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Sym(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_str (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Str(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_exp (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
if let Exp(depth, _) = token.value {
|
||||
token.value = Exp(depth, TokenIter::new(token.slice_source_exp(self.source)));
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
pub const fn grow_in (self) -> Self {
|
||||
let mut token = self.grow_exp();
|
||||
if let Value::Exp(depth, source) = token.value {
|
||||
token.value = Value::Exp(depth.saturating_add(1), source)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
pub const fn grow_out (self) -> Self {
|
||||
let mut token = self.grow_exp();
|
||||
if let Value::Exp(depth, source) = token.value {
|
||||
if depth > 0 {
|
||||
token.value = Value::Exp(depth - 1, source)
|
||||
} else {
|
||||
return self.error(Unexpected(')'))
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait TryFromDsl<'state, T>: Sized {
|
||||
fn try_from_expr <'source: 'state> (
|
||||
_state: &'state T, _iter: TokenIter<'source>
|
||||
) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
fn try_from_atom <'source: 'state> (
|
||||
state: &'state T, value: Value<'source>
|
||||
) -> Option<Self> {
|
||||
if let Exp(0, iter) = value {
|
||||
return Self::try_from_expr(state, iter.clone())
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryIntoDsl<T>: Sized {
|
||||
fn try_into_atom (&self) -> Option<Value>;
|
||||
}
|
||||
|
||||
/// Map EDN tokens to parameters of a given type for a given context
|
||||
pub trait Context<'state, U>: Sized {
|
||||
fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state, T: Context<'state, U>, U> Context<'state, U> for &T {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
(*self).get(iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option<T> {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
|
||||
self.as_ref().map(|s|s.get(iter)).flatten()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
use crate::*;
|
||||
use thiserror::Error;
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
#[derive(Error, Debug, Copy, Clone, PartialEq)] pub enum ParseError {
|
||||
#[error("parse failed: not implemented")]
|
||||
Unimplemented,
|
||||
#[error("parse failed: empty")]
|
||||
Empty,
|
||||
#[error("parse failed: incomplete")]
|
||||
Incomplete,
|
||||
#[error("parse failed: unexpected character '{0}'")]
|
||||
Unexpected(char),
|
||||
#[error("parse failed: error #{0}")]
|
||||
Code(u8),
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
//! The token iterator [TokenIter] allows you to get the
|
||||
//! general-purpose syntactic [Token]s represented by the source text.
|
||||
//!
|
||||
//! Both iterators are `peek`able:
|
||||
//!
|
||||
//! ```
|
||||
//! let src = include_str!("../test.edn");
|
||||
//! let mut view = tengri_dsl::TokenIter::new(src);
|
||||
//! assert_eq!(view.0.0, src);
|
||||
//! assert_eq!(view.peek(), view.0.peek())
|
||||
//! ```
|
||||
use crate::*;
|
||||
|
||||
/// Provides a native [Iterator] API over the [ConstIntoIter] [SourceIter]
|
||||
/// [TokenIter::next] returns just the [Token] and mutates `self`,
|
||||
/// instead of returning an updated version of the struct as [SourceIter::next] does.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct TokenIter<'a>(
|
||||
pub SourceIter<'a>
|
||||
);
|
||||
|
||||
impl<'a> TokenIter<'a> {
|
||||
pub const fn new (source: &'a str) -> Self {
|
||||
Self(SourceIter::new(source))
|
||||
}
|
||||
pub const fn peek (&self) -> Option<Token<'a>> {
|
||||
self.0.peek()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TokenIter<'a> {
|
||||
type Item = Token<'a>;
|
||||
fn next (&mut self) -> Option<Token<'a>> {
|
||||
self.0.next().map(|(item, rest)|{self.0 = rest; item})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for TokenIter<'a> {
|
||||
fn from (source: &'a str) -> Self{
|
||||
Self(SourceIter(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<SourceIter<'a>> for TokenIter<'a> {
|
||||
fn from (source: SourceIter<'a>) -> Self{
|
||||
Self(source)
|
||||
}
|
||||
}
|
||||
|
||||
/// Owns a reference to the source text.
|
||||
/// [SourceIter::next] emits subsequent pairs of:
|
||||
/// * a [Token] and
|
||||
/// * the source text remaining
|
||||
/// * [ ] TODO: maybe [SourceIter::next] should wrap the remaining source in `Self` ?
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct SourceIter<'a>(pub &'a str);
|
||||
|
||||
const_iter!(<'a>|self: SourceIter<'a>| => Token<'a> => self.next_mut().map(|(result, _)|result));
|
||||
|
||||
impl<'a> From<&'a str> for SourceIter<'a> {
|
||||
fn from (source: &'a str) -> Self{
|
||||
Self::new(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SourceIter<'a> {
|
||||
pub const fn new (source: &'a str) -> Self {
|
||||
Self(source)
|
||||
}
|
||||
pub const fn chomp (&self, index: usize) -> Self {
|
||||
Self(split_at(self.0, index).1)
|
||||
}
|
||||
pub const fn next (mut self) -> Option<(Token<'a>, Self)> {
|
||||
Self::next_mut(&mut self)
|
||||
}
|
||||
pub const fn peek (&self) -> Option<Token<'a>> {
|
||||
peek_src(self.0)
|
||||
}
|
||||
pub const fn next_mut (&mut self) -> Option<(Token<'a>, Self)> {
|
||||
match self.peek() {
|
||||
Some(token) => Some((token, self.chomp(token.end()))),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn peek_src <'a> (source: &'a str) -> Option<Token<'a>> {
|
||||
let mut token: Token<'a> = Token::new(source, 0, 0, Nil);
|
||||
iterate!(char_indices(source) => (start, c) => token = match token.value() {
|
||||
Err(_) => return Some(token),
|
||||
Nil => match c {
|
||||
' '|'\n'|'\r'|'\t' =>
|
||||
token.grow(),
|
||||
'(' =>
|
||||
Token::new(source, start, 1, Exp(1, TokenIter::new(str_range(source, start, start + 1)))),
|
||||
'"' =>
|
||||
Token::new(source, start, 1, Str(str_range(source, start, start + 1))),
|
||||
':'|'@' =>
|
||||
Token::new(source, start, 1, Sym(str_range(source, start, start + 1))),
|
||||
'/'|'a'..='z' =>
|
||||
Token::new(source, start, 1, Key(str_range(source, start, start + 1))),
|
||||
'0'..='9' =>
|
||||
Token::new(source, start, 1, match to_digit(c) {
|
||||
Ok(c) => Value::Num(c),
|
||||
Result::Err(e) => Value::Err(e)
|
||||
}),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Str(_) => match c {
|
||||
'"' => return Some(token),
|
||||
_ => token.grow_str(),
|
||||
},
|
||||
Num(n) => match c {
|
||||
'0'..='9' => token.grow_num(n, c),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Sym(_) => match c {
|
||||
'a'..='z'|'A'..='Z'|'0'..='9'|'-' => token.grow_sym(),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Key(_) => match c {
|
||||
'a'..='z'|'0'..='9'|'-'|'/' => token.grow_key(),
|
||||
' '|'\n'|'\r'|'\t'|')' => return Some(token),
|
||||
_ => token.error(Unexpected(c))
|
||||
},
|
||||
Exp(depth, _) => match depth {
|
||||
0 => return Some(token.grow_exp()),
|
||||
_ => match c {
|
||||
')' => token.grow_out(),
|
||||
'(' => token.grow_in(),
|
||||
_ => token.grow_exp(),
|
||||
}
|
||||
},
|
||||
});
|
||||
match token.value() {
|
||||
Nil => None,
|
||||
_ => Some(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn to_number (digits: &str) -> Result<usize, ParseError> {
|
||||
let mut value = 0;
|
||||
iterate!(char_indices(digits) => (_, c) => match to_digit(c) {
|
||||
Ok(digit) => value = 10 * value + digit,
|
||||
Result::Err(e) => return Result::Err(e)
|
||||
});
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub const fn to_digit (c: char) -> Result<usize, ParseError> {
|
||||
Ok(match c {
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
|
||||
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
_ => return Result::Err(Unexpected(c))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/// Static iteration helper.
|
||||
#[macro_export] macro_rules! iterate {
|
||||
($expr:expr => $arg: pat => $body:expr) => {
|
||||
let mut iter = $expr;
|
||||
while let Some(($arg, next)) = iter.next() {
|
||||
$body;
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the const iterator pattern.
|
||||
#[macro_export] macro_rules! const_iter {
|
||||
($(<$l:lifetime>)?|$self:ident: $Struct:ty| => $Item:ty => $expr:expr) => {
|
||||
impl$(<$l>)? Iterator for $Struct {
|
||||
type Item = $Item;
|
||||
fn next (&mut $self) -> Option<$Item> { $expr }
|
||||
}
|
||||
impl$(<$l>)? ConstIntoIter for $Struct {
|
||||
type Kind = IsIteratorKind;
|
||||
type Item = $Item;
|
||||
type IntoIter = Self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! get_value {
|
||||
($state:expr => $token:expr) => {
|
||||
if let Some(value) = $state.get(&$token.value) {
|
||||
value
|
||||
} else {
|
||||
panic!("no value corresponding to {:?}", &$token.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! get_content {
|
||||
($state:expr => $token:expr) => {
|
||||
if let Some(content) = $state.get_content(&$token.value) {
|
||||
content
|
||||
} else {
|
||||
panic!("no content corresponding to {:?}", &$token.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
//! [Token]s are parsed substrings with an associated [Value].
|
||||
//!
|
||||
//! * [ ] FIXME: Value may be [Err] which may shadow [Result::Err]
|
||||
//! * [Value::Exp] wraps an expression depth and a [SourceIter]
|
||||
//! with the remaining part of the expression.
|
||||
//! * expression depth other that 0 mean unclosed parenthesis.
|
||||
//! * closing and unopened parenthesis panics during reading.
|
||||
//! * [ ] TODO: signed depth might be interesting
|
||||
//! * [Value::Sym] and [Value::Key] are stringish literals
|
||||
//! with slightly different parsing rules.
|
||||
//! * [Value::Num] is an unsigned integer literal.
|
||||
//!```
|
||||
//! use tengri_dsl::{*, Value::*};
|
||||
//! let source = include_str!("../test.edn");
|
||||
//! let mut view = TokenIter::new(source);
|
||||
//! assert_eq!(view.peek(), Some(Token {
|
||||
//! source,
|
||||
//! start: 0,
|
||||
//! length: source.len(),
|
||||
//! value: Exp(0, TokenIter::new(&source[1..]))
|
||||
//! }));
|
||||
//!```
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> {
|
||||
pub source: &'source str,
|
||||
pub start: usize,
|
||||
pub length: usize,
|
||||
pub value: Value<'source>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> {
|
||||
#[default] Nil,
|
||||
Err(ParseError),
|
||||
Num(usize),
|
||||
Sym(&'source str),
|
||||
Key(&'source str),
|
||||
Str(&'source str),
|
||||
Exp(usize, TokenIter<'source>),
|
||||
}
|
||||
|
||||
impl<'source> Token<'source> {
|
||||
pub const fn new (source: &'source str, start: usize, length: usize, value: Value<'source>) -> Self {
|
||||
Self { source, start, length, value }
|
||||
}
|
||||
pub const fn end (&self) -> usize {
|
||||
self.start.saturating_add(self.length)
|
||||
}
|
||||
pub const fn slice (&'source self) -> &'source str {
|
||||
self.slice_source(self.source)
|
||||
//str_range(self.source, self.start, self.end())
|
||||
}
|
||||
pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start, self.end())
|
||||
}
|
||||
pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start.saturating_add(1), self.end())
|
||||
}
|
||||
pub const fn value (&self) -> Value {
|
||||
self.value
|
||||
}
|
||||
pub const fn error (self, error: ParseError) -> Self {
|
||||
Self { value: Value::Err(error), ..self }
|
||||
}
|
||||
pub const fn grow (self) -> Self {
|
||||
Self { length: self.length.saturating_add(1), ..self }
|
||||
}
|
||||
pub const fn grow_num (self, m: usize, c: char) -> Self {
|
||||
match to_digit(c) {
|
||||
Ok(n) => Self { value: Num(10*m+n), ..self.grow() },
|
||||
Result::Err(e) => Self { value: Err(e), ..self.grow() },
|
||||
}
|
||||
}
|
||||
pub const fn grow_key (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Key(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_sym (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Sym(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_str (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
token.value = Str(token.slice_source(self.source));
|
||||
token
|
||||
}
|
||||
pub const fn grow_exp (self) -> Self {
|
||||
let mut token = self.grow();
|
||||
if let Exp(depth, _) = token.value {
|
||||
token.value = Exp(depth, TokenIter::new(token.slice_source_exp(self.source)));
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
pub const fn grow_in (self) -> Self {
|
||||
let mut token = self.grow_exp();
|
||||
if let Value::Exp(depth, source) = token.value {
|
||||
token.value = Value::Exp(depth.saturating_add(1), source)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
pub const fn grow_out (self) -> Self {
|
||||
let mut token = self.grow_exp();
|
||||
if let Value::Exp(depth, source) = token.value {
|
||||
if depth > 0 {
|
||||
token.value = Value::Exp(depth - 1, source)
|
||||
} else {
|
||||
return self.error(Unexpected(')'))
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
token
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,36 @@
|
|||
//! [Token]s are parsed substrings with an associated [Value].
|
||||
//!
|
||||
//! * [ ] FIXME: Value may be [Err] which may shadow [Result::Err]
|
||||
//! * [Value::Exp] wraps an expression depth and a [SourceIter]
|
||||
//! with the remaining part of the expression.
|
||||
//! * expression depth other that 0 mean unclosed parenthesis.
|
||||
//! * closing and unopened parenthesis panics during reading.
|
||||
//! * [ ] TODO: signed depth might be interesting
|
||||
//! * [Value::Sym] and [Value::Key] are stringish literals
|
||||
//! with slightly different parsing rules.
|
||||
//! * [Value::Num] is an unsigned integer literal.
|
||||
//!```
|
||||
//! use tengri_dsl::{*, Value::*};
|
||||
//! let source = include_str!("../test.edn");
|
||||
//! let mut view = TokenIter::new(source);
|
||||
//! assert_eq!(view.peek(), Some(Token {
|
||||
//! source,
|
||||
//! start: 0,
|
||||
//! length: source.len(),
|
||||
//! value: Exp(0, TokenIter::new(&source[1..]))
|
||||
//! }));
|
||||
//!```
|
||||
//! The token iterator [TokenIter] allows you to get the
|
||||
//! general-purpose syntactic [Token]s represented by the source text.
|
||||
//!
|
||||
//! Both iterators are `peek`able:
|
||||
//!
|
||||
//! ```
|
||||
//! let src = include_str!("../test.edn");
|
||||
//! let mut view = tengri_dsl::TokenIter::new(src);
|
||||
//! assert_eq!(view.0.0, src);
|
||||
//! assert_eq!(view.peek(), view.0.peek())
|
||||
//! ```
|
||||
#![feature(adt_const_params)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(impl_trait_in_fn_trait_return)]
|
||||
|
|
@ -8,11 +41,7 @@ pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind};
|
|||
pub(crate) use konst::string::{split_at, str_range, char_indices};
|
||||
pub(crate) use std::fmt::Debug;
|
||||
|
||||
mod dsl_error; pub use self::dsl_error::*;
|
||||
mod dsl_token; pub use self::dsl_token::*;
|
||||
mod dsl_iter; pub use self::dsl_iter::*;
|
||||
mod dsl_context; pub use self::dsl_context::*;
|
||||
mod dsl_macros;
|
||||
mod dsl; pub use self::dsl::*;
|
||||
|
||||
#[cfg(test)] mod test_token_iter {
|
||||
use crate::*;
|
||||
|
|
@ -114,22 +143,6 @@ mod dsl_macros;
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_dsl_context () {
|
||||
struct Test;
|
||||
#[tengri_proc::expose]
|
||||
impl Test {
|
||||
fn some_bool (&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
assert_eq!(Test.get(&Value::Sym(":false")), Some(false));
|
||||
assert_eq!(Test.get(&Value::Sym(":true")), Some(true));
|
||||
assert_eq!(Test.get(&Value::Sym(":some-bool")), Some(true));
|
||||
assert_eq!(Test.get(&Value::Sym(":missing-bool")), None);
|
||||
assert_eq!(Test.get(&Value::Num(0)), Some(false));
|
||||
assert_eq!(Test.get(&Value::Num(1)), Some(true));
|
||||
}
|
||||
|
||||
//#[cfg(test)] #[test] fn test_examples () -> Result<(), ParseError> {
|
||||
//// Let's pretend to render some view.
|
||||
//let source = include_str!("../../tek/src/view_arranger.edn");
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f
|
|||
match exp_iter.next() {
|
||||
Some(Token { value: Value::Sym(binding), .. }) => {
|
||||
if input.matches_dsl(binding) {
|
||||
if let Some(command) = C::try_from_expr(state, exp_iter.clone()) {
|
||||
if let Some(command) = C::try_from_expr(state, &mut exp_iter) {
|
||||
return Some(command)
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f
|
|||
match exp_iter.next() {
|
||||
Some(Token { value: Value::Sym(binding), .. }) => {
|
||||
if input.matches_dsl(binding) {
|
||||
if let Some(command) = C::try_from_expr(state, exp_iter.clone()) {
|
||||
if let Some(command) = C::try_from_expr(state, &mut exp_iter) {
|
||||
return Some(command)
|
||||
}
|
||||
}
|
||||
|
|
@ -132,8 +132,7 @@ where
|
|||
M: KeyMap<'state, S, C, I> + Send + Sync
|
||||
{
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "[InputMap: {} layer(s)]", self.layers.len());
|
||||
Ok(())
|
||||
write!(f, "[InputMap: {} layer(s)]", self.layers.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,17 @@ pub trait Input: Send + Sync + Sized {
|
|||
/// Implement the [Handle] trait.
|
||||
#[macro_export] macro_rules! handle {
|
||||
(|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||
impl<E: Engine> Handle<E> for $Struct {
|
||||
impl<E: Engine> ::tengri::input::Handle<E> for $Struct {
|
||||
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
|
||||
$handler
|
||||
}
|
||||
}
|
||||
};
|
||||
($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||
impl Handle<$E> for $Struct {
|
||||
fn handle (&mut $self, $input: &$E) -> Perhaps<<$E as Input>::Handled> {
|
||||
impl ::tengri::input::Handle<$E> for $Struct {
|
||||
fn handle (&mut $self, $input: &$E) ->
|
||||
Perhaps<<$E as ::tengri::input::Input>::Handled>
|
||||
{
|
||||
$handler
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
|||
#[cfg(test)] mod test {
|
||||
use crate::*;
|
||||
use proptest::{prelude::*, option::of};
|
||||
use proptest::option::of;
|
||||
|
||||
proptest! {
|
||||
#[test] fn proptest_direction (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use crate::*;
|
||||
pub use Direction::*;
|
||||
/// A split or layer.
|
||||
pub struct Bsp<X, Y>(Direction, X, Y);
|
||||
pub struct Bsp<A, B>(
|
||||
pub(crate) Direction,
|
||||
pub(crate) A,
|
||||
pub(crate) B,
|
||||
);
|
||||
impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
|
||||
fn layout (&self, outer: E::Area) -> E::Area {
|
||||
let [_, _, c] = self.areas(outer);
|
||||
|
|
|
|||
|
|
@ -22,18 +22,13 @@ impl<A, B> Either<A, B> {
|
|||
try_from_expr!(<'source, 'state, E>: When<RenderBox<'state, E>>: |state, iter| {
|
||||
if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() {
|
||||
let _ = iter.next().unwrap();
|
||||
|
||||
let condition = iter.next().expect("no condition specified");
|
||||
let condition = state.get(&mut iter).expect("no condition provided");
|
||||
|
||||
let content = iter.next().expect("no content specified");
|
||||
let content = if let Some(content) = state.get_content(&content.value) {
|
||||
content
|
||||
} else {
|
||||
panic!("no content corresponding to for {:?}", &content);
|
||||
};
|
||||
|
||||
return Some(Self(condition, content))
|
||||
let content = iter.next().expect("no content specified").value;
|
||||
return Some(Self(
|
||||
state.get(&mut iter)
|
||||
.expect("no condition provided"),
|
||||
state.get_content(&content)
|
||||
.unwrap_or_else(||panic!("no content corresponding to for {:?}", &content))
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -41,25 +36,16 @@ try_from_expr!(<'source, 'state, E>: When<RenderBox<'state, E>>: |state, iter| {
|
|||
try_from_expr!(<'source, 'state, E>: Either<RenderBox<'state, E>, RenderBox<'state, E>>: |state, iter| {
|
||||
if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() {
|
||||
let _ = iter.next().unwrap();
|
||||
|
||||
let condition = iter.next().expect("no condition specified");
|
||||
let condition = state.get(&mut iter).expect("no condition provided");
|
||||
|
||||
let content = iter.next().expect("no content specified");
|
||||
let content = if let Some(content) = state.get_content(&content.value) {
|
||||
content
|
||||
} else {
|
||||
panic!("no content 1 corresponding to {:?}", &content);
|
||||
};
|
||||
|
||||
let alternate = iter.next().expect("no alternate specified");
|
||||
let alternate = if let Some(alternate) = state.get_content(&alternate.value) {
|
||||
alternate
|
||||
} else {
|
||||
panic!("no content 2 corresponding to {:?}", &alternate);
|
||||
};
|
||||
|
||||
return Some(Self(condition, content, alternate))
|
||||
let content = iter.next().expect("no content specified").value;
|
||||
let alternate = iter.next().expect("no alternate specified").value;
|
||||
return Some(Self(
|
||||
state.get(&mut iter)
|
||||
.expect("no condition provided"),
|
||||
state.get_content(&content)
|
||||
.unwrap_or_else(||panic!("no content 1 corresponding to {:?}", &content)),
|
||||
state.get_content(&alternate)
|
||||
.unwrap_or_else(||panic!("no content 2 corresponding to {:?}", &alternate)),
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ macro_rules! transform_xy {
|
|||
#[cfg(feature = "dsl")]
|
||||
impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
|
||||
for $Enum<RenderBox<'state, E>> {
|
||||
fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>)
|
||||
fn try_from_expr <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>)
|
||||
-> Option<Self>
|
||||
{
|
||||
let mut iter = iter.clone();
|
||||
|
|
@ -43,7 +43,11 @@ macro_rules! transform_xy {
|
|||
if k == $x || k == $y || k == $xy {
|
||||
let _ = iter.next().unwrap();
|
||||
let token = iter.next().expect("no content specified");
|
||||
let content = get_content!(state => token);
|
||||
let content = if let Some(content) = state.get_content(&token.value) {
|
||||
content
|
||||
} else {
|
||||
panic!("no content corresponding to {:?}", &token.value);
|
||||
};
|
||||
return Some(match k {
|
||||
$x => Self::x(content),
|
||||
$y => Self::y(content),
|
||||
|
|
@ -84,7 +88,7 @@ macro_rules! transform_xy_unit {
|
|||
#[cfg(feature = "dsl")]
|
||||
impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
|
||||
for $Enum<E::Unit, RenderBox<'state, E>> {
|
||||
fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>) -> Option<Self> {
|
||||
fn try_from_expr <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Option<Self> {
|
||||
let mut iter = iter.clone();
|
||||
if let Some(Token { value: Value::Key(k), .. }) = iter.peek() {
|
||||
if k == $x || k == $y {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ pub trait ViewContext<'state, E: Output + 'state>: Send + Sync
|
|||
> TryFromDsl<$lt_state, T> for $Struct {
|
||||
fn try_from_expr <$lt_source: $lt_state> (
|
||||
$state: &$lt_state T,
|
||||
$iter: TokenIter<$lt_source>
|
||||
$iter: &mut TokenIter<$lt_source>
|
||||
) -> Option<Self> {
|
||||
let mut $iter = $iter.clone();
|
||||
$body;
|
||||
|
|
|
|||
204
proc/src/lib.rs
204
proc/src/lib.rs
|
|
@ -2,23 +2,17 @@
|
|||
#![feature(box_patterns)]
|
||||
extern crate proc_macro;
|
||||
|
||||
pub(crate) use std::collections::{BTreeMap, BTreeSet};
|
||||
pub(crate) use std::collections::BTreeMap;
|
||||
pub(crate) use std::cmp::Ordering;
|
||||
pub(crate) use std::sync::Arc;
|
||||
pub(crate) use proc_macro::TokenStream;
|
||||
pub(crate) use proc_macro2::{
|
||||
TokenStream as TokenStream2, TokenTree,
|
||||
Ident, Span, Punct, Spacing::*, Group, Delimiter, Literal
|
||||
TokenStream as TokenStream2, Ident, Span, Punct, Group, Delimiter, Spacing::*
|
||||
};
|
||||
pub(crate) use syn::{
|
||||
parse, parse_macro_input, parse_quote as pq,
|
||||
braced, bracketed, parenthesized, Token,
|
||||
Arm, Expr, Attribute, Meta, MetaList, Path, PathSegment, PathArguments,
|
||||
ImplItem, ImplItemFn, LitStr, Type, ItemImpl, ReturnType, Signature, FnArg,
|
||||
Pat, PatType, PatIdent,
|
||||
parse_macro_input, ImplItem, ImplItemFn, LitStr, Type,
|
||||
ItemImpl, ReturnType, Signature, FnArg, Pat, PatType, PatIdent,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
token::{PathSep, Brace},
|
||||
punctuated::Punctuated,
|
||||
};
|
||||
pub(crate) use quote::{quote, TokenStreamExt, ToTokens};
|
||||
pub(crate) use heck::{AsKebabCase, AsUpperCamelCase};
|
||||
|
|
@ -27,14 +21,7 @@ mod proc_view;
|
|||
mod proc_expose;
|
||||
mod proc_command;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn view (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||
use self::proc_view::{ViewDef, ViewMeta, ViewImpl};
|
||||
write(ViewDef(
|
||||
parse_macro_input!(meta as ViewMeta),
|
||||
parse_macro_input!(item as ViewImpl),
|
||||
))
|
||||
}
|
||||
#[cfg(test)] use syn::parse_quote as pq;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn expose (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
|
@ -54,6 +41,15 @@ pub fn command (meta: TokenStream, item: TokenStream) -> TokenStream {
|
|||
))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn view (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||
use self::proc_view::{ViewDef, ViewMeta, ViewImpl};
|
||||
write(ViewDef(
|
||||
parse_macro_input!(meta as ViewMeta),
|
||||
parse_macro_input!(item as ViewImpl),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn write <T: ToTokens> (t: T) -> TokenStream {
|
||||
let mut out = TokenStream2::new();
|
||||
t.to_tokens(&mut out);
|
||||
|
|
@ -73,3 +69,175 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
|
|||
out.append(token);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_proc_view () {
|
||||
let x: crate::proc_view::ViewMeta = pq! { SomeOutput };
|
||||
let output: Ident = pq! { SomeOutput };
|
||||
assert_eq!(x.output, output);
|
||||
|
||||
// TODO
|
||||
let x: crate::proc_view::ViewImpl = pq! {
|
||||
impl Foo {
|
||||
/// docstring1
|
||||
#[tengri::view(":view1")] #[bar] fn a_view () {}
|
||||
|
||||
#[baz]
|
||||
/// docstring2
|
||||
#[baz] fn is_not_view () {}
|
||||
}
|
||||
};
|
||||
let expected_target: Ident = pq! { Foo };
|
||||
//assert_eq!(x.target, expected_target);
|
||||
//assert_eq!(x.items.len(), 2);
|
||||
//assert_eq!(x.items[0].item, pq! {
|
||||
///// docstring1
|
||||
//#[bar] fn a_view () {}
|
||||
//});
|
||||
//assert_eq!(x.items[1].item, pq! {
|
||||
//#[baz]
|
||||
///// docstring2
|
||||
//#[baz] fn is_not_view () {}
|
||||
//});
|
||||
//assert_eq!(x.syms, vec![
|
||||
//ViewArm( { symbol: pq! { ":view1" }, name: pq! { a_view }, },
|
||||
//]);
|
||||
// FIXME
|
||||
//let parsed: ViewDefinition = pq! {
|
||||
//#[tengri_proc::view(SomeOutput)]
|
||||
//impl SomeView {
|
||||
//#[tengri::view(":view-1")]
|
||||
//fn view_1 (&self) -> impl Content<SomeOutput> + use<'_> {
|
||||
//"view-1"
|
||||
//}
|
||||
//}
|
||||
//};
|
||||
//let written = quote! { #parsed };
|
||||
//assert_eq!(format!("{written}"), format!("{}", quote! {
|
||||
//impl SomeView {
|
||||
//fn view_1 (&self) -> impl Content<SomeOutput> + use<'_> {
|
||||
//"view-1"
|
||||
//}
|
||||
//}
|
||||
///// Generated by [tengri_proc].
|
||||
//impl ::tengri::output::Content<SomeOutput> for SomeView {
|
||||
//fn content (&self) -> impl Render<SomeOutput> {
|
||||
//self.size.of(::tengri::output::View(self, self.config.view))
|
||||
//}
|
||||
//}
|
||||
///// Generated by [tengri_proc].
|
||||
//impl<'a> ::tengri::dsl::ViewContext<'a, SomeOutput> for SomeView {
|
||||
//fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, SomeOutput>> {
|
||||
//match value {
|
||||
//::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(),
|
||||
//_ => panic!("expected Sym(content), got: {value:?}")
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}));
|
||||
}
|
||||
|
||||
//#[cfg(test)] #[test] fn test_expose_definition () {
|
||||
// TODO
|
||||
//let parsed: ExposeImpl = pq! {
|
||||
////#[tengri_proc::expose]
|
||||
//impl Something {
|
||||
//fn something () -> bool {}
|
||||
//}
|
||||
//};
|
||||
//// FIXME:
|
||||
////assert_eq!(
|
||||
////format!("{}", quote! { #parsed }),
|
||||
////format!("{}", quote! {
|
||||
////impl Something {
|
||||
////fn something () {}
|
||||
////}
|
||||
////impl ::tengri::Context<bool> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":true") => true,
|
||||
////::tengri::Value::Sym(":false") => false,
|
||||
////::tengri::Value::Sym(":bool1") => true || false,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////})
|
||||
////);
|
||||
|
||||
//let parsed: ExposeImpl = pq! {
|
||||
////#[tengri_proc::expose]
|
||||
//impl Something {
|
||||
//#[tengri::expose(bool)] {
|
||||
//":bool1" => true || false,
|
||||
//}
|
||||
//#[tengri::expose(u16)] {
|
||||
//":u161" => 0 + 1,
|
||||
//}
|
||||
//#[tengri::expose(usize)] {
|
||||
//":usize1" => 1 + 2,
|
||||
//}
|
||||
//#[tengri::expose(Arc<str>)] {
|
||||
//":arcstr1" => "foo".into(),
|
||||
//}
|
||||
//#[tengri::expose(Option<Arc<str>>)] {
|
||||
//":optarcstr1" => Some("bar".into()),
|
||||
//":optarcstr2" => Some("baz".into()),
|
||||
//}
|
||||
//fn something () {}
|
||||
//}
|
||||
//};
|
||||
//// FIXME:
|
||||
////assert_eq!(
|
||||
////format!("{}", quote! { #parsed }),
|
||||
////format!("{}", quote! {
|
||||
////impl Something {
|
||||
////fn something () {}
|
||||
////}
|
||||
////impl ::tengri::Context<Arc<str>> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<Arc<str>> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":arcstr1") => "foo".into(),
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<Option<Arc<str>>> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<Option<Arc<str>>> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":optarcstr1") => Some("bar".into()),
|
||||
////::tengri::Value::Sym(":optarcstr2") => Some("baz".into()),
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<bool> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":true") => true,
|
||||
////::tengri::Value::Sym(":false") => false,
|
||||
////::tengri::Value::Sym(":bool1") => true || false,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<u16> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<u16> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Num(n) => *n as u16,
|
||||
////::tengri::Value::Sym(":u161") => 0 + 1,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<usize> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<usize> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Num(n) => *n as usize,
|
||||
////::tengri::Value::Sym(":usize1") => 1 + 2,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////})
|
||||
////)
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ impl ToTokens for CommandDef {
|
|||
}
|
||||
#block
|
||||
/// Generated by [tengri_proc].
|
||||
impl<'state> TryFromDsl<'state, #target> for #enumeration {
|
||||
impl<'state> ::tengri::dsl::TryFromDsl<'state, #target> for #enumeration {
|
||||
fn try_from_expr <'source: 'state> (
|
||||
state: &'state #target, iter: TokenIter<'source>
|
||||
state: &'state #target, iter: &mut ::tengri::dsl::TokenIter<'source>
|
||||
) -> Option<Self> {
|
||||
let mut iter = iter.clone();
|
||||
match iter.next() {
|
||||
|
|
@ -80,7 +80,15 @@ impl ToTokens for CommandDef {
|
|||
}
|
||||
}
|
||||
/// Generated by [tengri_proc].
|
||||
impl Command<#target> for #enumeration {
|
||||
impl<'state> ::tengri::dsl::Context<'state, #enumeration> for #target {
|
||||
fn get <'source> (&self, iter: &mut ::tengri::dsl::TokenIter<'source>)
|
||||
-> Option<#enumeration>
|
||||
{
|
||||
#enumeration::try_from_expr(self, iter)
|
||||
}
|
||||
}
|
||||
/// Generated by [tengri_proc].
|
||||
impl ::tengri::input::Command<#target> for #enumeration {
|
||||
fn execute (self, state: &mut #target) -> Perhaps<Self> {
|
||||
match self {
|
||||
#(#implementations)*
|
||||
|
|
@ -121,13 +129,12 @@ impl CommandArm {
|
|||
Some((arg, ty))
|
||||
} else {
|
||||
unreachable!("only typed args should be present at this position");
|
||||
None
|
||||
})
|
||||
}
|
||||
fn to_enum_variant_def (&self) -> TokenStream2 {
|
||||
let mut out = TokenStream2::new();
|
||||
out.append(self.to_enum_variant_ident());
|
||||
let ident = &self.0;
|
||||
//let ident = &self.0;
|
||||
if self.has_args() {
|
||||
out.append(Group::new(Delimiter::Brace, {
|
||||
let mut out = TokenStream2::new();
|
||||
|
|
@ -148,12 +155,12 @@ impl CommandArm {
|
|||
out.append(Group::new(Delimiter::Brace, {
|
||||
let mut out = TokenStream2::new();
|
||||
for (arg, ty) in self.args() {
|
||||
let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})",
|
||||
quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
|
||||
//let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})",
|
||||
//quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
|
||||
let give_err = LitStr::new(&format!("{}: missing value for \"{}\" ({})",
|
||||
quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
|
||||
write_quote_to(&mut out, quote! {
|
||||
#arg: Context::get(state, &mut iter).expect(#give_err),
|
||||
#arg: ::tengri::dsl::Context::get(state, &mut iter).expect(#give_err),
|
||||
});
|
||||
}
|
||||
out
|
||||
|
|
@ -164,11 +171,11 @@ impl CommandArm {
|
|||
fn to_enum_variant_unbind (&self) -> TokenStream2 {
|
||||
let mut out = TokenStream2::new();
|
||||
out.append(self.to_enum_variant_ident());
|
||||
let ident = &self.0;
|
||||
//let ident = &self.0;
|
||||
if self.has_args() {
|
||||
out.append(Group::new(Delimiter::Brace, {
|
||||
let mut out = TokenStream2::new();
|
||||
for (arg, ty) in self.args() {
|
||||
for (arg, _ty) in self.args() {
|
||||
write_quote_to(&mut out, quote! { #arg , });
|
||||
}
|
||||
out
|
||||
|
|
@ -189,8 +196,10 @@ impl CommandArm {
|
|||
fn to_implementation (&self) -> TokenStream2 {
|
||||
let ident = &self.0;
|
||||
let variant = self.to_enum_variant_unbind();
|
||||
let mut give_rest = write_quote(quote! { /*TODO*/ });
|
||||
let give_args = self.args().map(|(arg, ty)|write_quote(quote! { #arg, })).collect::<Vec<_>>();
|
||||
let give_rest = write_quote(quote! { /*TODO*/ });
|
||||
let give_args = self.args()
|
||||
.map(|(arg, _ty)|write_quote(quote! { #arg, }))
|
||||
.collect::<Vec<_>>();
|
||||
write_quote(quote! { Self::#variant => Self::#ident(state, #(#give_args)* #give_rest), })
|
||||
}
|
||||
}
|
||||
|
|
@ -216,35 +225,3 @@ impl ToTokens for CommandVariant {
|
|||
out.append(Punct::new(',', Alone));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for CommandArm {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(ident, args, returnType) = self;
|
||||
for ident in ["tengri", "dsl", "Value", "Sym"].iter() {
|
||||
out.append(Punct::new(':', Joint));
|
||||
out.append(Punct::new(':', Alone));
|
||||
out.append(Ident::new(ident, Span::call_site()));
|
||||
}
|
||||
out.append(Group::new(Delimiter::Parenthesis, {
|
||||
let mut out = TokenStream2::new();
|
||||
out.append(self.to_enum_variant_ident());
|
||||
out
|
||||
}));
|
||||
out.append(Punct::new('=', Joint));
|
||||
out.append(Punct::new('>', Alone));
|
||||
out.append(Ident::new("Self", Span::call_site()));
|
||||
out.append(Punct::new(':', Joint));
|
||||
out.append(Punct::new(':', Alone));
|
||||
out.append(ident.clone());
|
||||
out.append(Group::new(Delimiter::Parenthesis, {
|
||||
let mut out = TokenStream2::new();
|
||||
for arg in args.iter() {
|
||||
// TODO
|
||||
out.append(LitStr::new(&self.to_key(), Span::call_site()).token());
|
||||
out.append(Punct::new(',', Alone));
|
||||
}
|
||||
out
|
||||
}));
|
||||
out.append(Punct::new(',', Alone));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct ExposeSym(LitStr);
|
|||
struct ExposeType(Box<Type>);
|
||||
|
||||
impl Parse for ExposeMeta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
fn parse (_input: ParseStream) -> Result<Self> {
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
|
@ -189,109 +189,3 @@ impl ToTokens for ExposeType {
|
|||
self.0.to_tokens(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_expose_definition () {
|
||||
// TODO
|
||||
//let parsed: ExposeImpl = pq! {
|
||||
////#[tengri_proc::expose]
|
||||
//impl Something {
|
||||
//fn something () -> bool {}
|
||||
//}
|
||||
//};
|
||||
//// FIXME:
|
||||
////assert_eq!(
|
||||
////format!("{}", quote! { #parsed }),
|
||||
////format!("{}", quote! {
|
||||
////impl Something {
|
||||
////fn something () {}
|
||||
////}
|
||||
////impl ::tengri::Context<bool> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":true") => true,
|
||||
////::tengri::Value::Sym(":false") => false,
|
||||
////::tengri::Value::Sym(":bool1") => true || false,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////})
|
||||
////);
|
||||
|
||||
//let parsed: ExposeImpl = pq! {
|
||||
////#[tengri_proc::expose]
|
||||
//impl Something {
|
||||
//#[tengri::expose(bool)] {
|
||||
//":bool1" => true || false,
|
||||
//}
|
||||
//#[tengri::expose(u16)] {
|
||||
//":u161" => 0 + 1,
|
||||
//}
|
||||
//#[tengri::expose(usize)] {
|
||||
//":usize1" => 1 + 2,
|
||||
//}
|
||||
//#[tengri::expose(Arc<str>)] {
|
||||
//":arcstr1" => "foo".into(),
|
||||
//}
|
||||
//#[tengri::expose(Option<Arc<str>>)] {
|
||||
//":optarcstr1" => Some("bar".into()),
|
||||
//":optarcstr2" => Some("baz".into()),
|
||||
//}
|
||||
//fn something () {}
|
||||
//}
|
||||
//};
|
||||
//// FIXME:
|
||||
////assert_eq!(
|
||||
////format!("{}", quote! { #parsed }),
|
||||
////format!("{}", quote! {
|
||||
////impl Something {
|
||||
////fn something () {}
|
||||
////}
|
||||
////impl ::tengri::Context<Arc<str>> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<Arc<str>> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":arcstr1") => "foo".into(),
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<Option<Arc<str>>> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<Option<Arc<str>>> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":optarcstr1") => Some("bar".into()),
|
||||
////::tengri::Value::Sym(":optarcstr2") => Some("baz".into()),
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<bool> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Sym(":true") => true,
|
||||
////::tengri::Value::Sym(":false") => false,
|
||||
////::tengri::Value::Sym(":bool1") => true || false,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<u16> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<u16> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Num(n) => *n as u16,
|
||||
////::tengri::Value::Sym(":u161") => 0 + 1,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////impl ::tengri::Context<usize> for Something {
|
||||
////fn get (&self, dsl: &::tengri::Value) -> Option<usize> {
|
||||
////Some(match dsl {
|
||||
////::tengri::Value::Num(n) => *n as usize,
|
||||
////::tengri::Value::Sym(":usize1") => 1 + 2,
|
||||
////_ => return None
|
||||
////})
|
||||
////}
|
||||
////}
|
||||
////})
|
||||
////)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ pub(crate) struct ViewDef(pub(crate) ViewMeta, pub(crate) ViewImpl);
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ViewMeta {
|
||||
output: Ident,
|
||||
pub(crate) output: Ident,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -45,7 +45,16 @@ impl ToTokens for ViewDef {
|
|||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self;
|
||||
let ident = &block.self_ty;
|
||||
let exposed: Vec<_> = exposed.iter().map(|(k,v)|ViewArm(k.clone(), v.clone())).collect();
|
||||
let mut available = vec![];
|
||||
let exposed: Vec<_> = exposed.iter().map(|(k,v)|{
|
||||
available.push(k.clone());
|
||||
ViewArm(k.clone(), v.clone())
|
||||
}).collect();
|
||||
let available: String = available.join(", ");
|
||||
let error_msg = LitStr::new(
|
||||
&format!("expected Sym(content), got: {{value:?}}, available: {available}"),
|
||||
Span::call_site()
|
||||
);
|
||||
for token in quote! {
|
||||
#block
|
||||
/// Generated by [tengri_proc].
|
||||
|
|
@ -59,10 +68,7 @@ impl ToTokens for ViewDef {
|
|||
fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>)
|
||||
-> Option<RenderBox<'state, #output>>
|
||||
{
|
||||
match value {
|
||||
#(#exposed)*
|
||||
_ => panic!("expected Sym(content), got: {value:?}")
|
||||
}
|
||||
match value { #(#exposed)* _ => panic!(#error_msg) }
|
||||
}
|
||||
}
|
||||
} {
|
||||
|
|
@ -108,132 +114,3 @@ impl ToTokens for ViewArm {
|
|||
out.append(Punct::new(',', Alone));
|
||||
}
|
||||
}
|
||||
|
||||
//impl ToTokens for ViewSym {
|
||||
//fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
//out.append(Punct::new(':', Joint));
|
||||
//out.append(Punct::new(':', Alone));
|
||||
//out.append(Ident::new("tengri", Span::call_site()));
|
||||
//out.append(Punct::new(':', Joint));
|
||||
//out.append(Punct::new(':', Alone));
|
||||
//out.append(Ident::new("dsl", Span::call_site()));
|
||||
//out.append(Punct::new(':', Joint));
|
||||
//out.append(Punct::new(':', Alone));
|
||||
//out.append(Ident::new("Value", Span::call_site()));
|
||||
//out.append(Punct::new(':', Joint));
|
||||
//out.append(Punct::new(':', Alone));
|
||||
//out.append(Ident::new("Sym", Span::call_site()));
|
||||
//out.append(Group::new(Delimiter::Parenthesis, {
|
||||
//let mut out = TokenStream2::new();
|
||||
//out.append(self.symbol.clone());
|
||||
//out
|
||||
//}));
|
||||
//out.append(Punct::new('=', Joint));
|
||||
//out.append(Punct::new('>', Alone));
|
||||
//out.append(Ident::new("Some", Span::call_site()));
|
||||
//out.append(Group::new(Delimiter::Parenthesis, {
|
||||
//let mut out = TokenStream2::new();
|
||||
//out.append(Ident::new("self", Span::call_site()));
|
||||
//out.append(Punct::new('.', Alone));
|
||||
//out.append(self.name.clone());
|
||||
//out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new()));
|
||||
//out.append(Punct::new('.', Alone));
|
||||
//out.append(Ident::new("boxed", Span::call_site()));
|
||||
//out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new()));
|
||||
//out
|
||||
//}));
|
||||
//out.append(Punct::new(',', Alone));
|
||||
//}
|
||||
//}
|
||||
|
||||
fn nth_segment_is (segments: &Punctuated<PathSegment, PathSep>, n: usize, x: &str) -> bool {
|
||||
if let Some(PathSegment { arguments: PathArguments::None, ident, .. }) = segments.get(n) {
|
||||
if format!("{ident}") == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//impl std::cmp::PartialEq for ViewItem {
|
||||
//fn eq (&self, other: &Self) -> bool {
|
||||
//self.item == other.item && (format!("{:?}", self.expose) == format!("{:?}", other.expose))
|
||||
//}
|
||||
//}
|
||||
|
||||
//impl std::cmp::PartialEq for ViewSym {
|
||||
//fn eq (&self, other: &Self) -> bool {
|
||||
//self.name == other.name && (format!("{}", self.symbol) == format!("{}", other.symbol))
|
||||
//}
|
||||
//}
|
||||
|
||||
#[cfg(test)] #[test] fn test_view_meta () {
|
||||
let x: ViewMeta = pq! { SomeOutput };
|
||||
let output: Ident = pq! { SomeOutput };
|
||||
assert_eq!(x.output, output);
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_view_impl () {
|
||||
// TODO
|
||||
let x: ViewImpl = pq! {
|
||||
impl Foo {
|
||||
/// docstring1
|
||||
#[tengri::view(":view1")] #[bar] fn a_view () {}
|
||||
|
||||
#[baz]
|
||||
/// docstring2
|
||||
#[baz] fn is_not_view () {}
|
||||
}
|
||||
};
|
||||
let expected_target: Ident = pq! { Foo };
|
||||
//assert_eq!(x.target, expected_target);
|
||||
//assert_eq!(x.items.len(), 2);
|
||||
//assert_eq!(x.items[0].item, pq! {
|
||||
///// docstring1
|
||||
//#[bar] fn a_view () {}
|
||||
//});
|
||||
//assert_eq!(x.items[1].item, pq! {
|
||||
//#[baz]
|
||||
///// docstring2
|
||||
//#[baz] fn is_not_view () {}
|
||||
//});
|
||||
//assert_eq!(x.syms, vec![
|
||||
//ViewArm( { symbol: pq! { ":view1" }, name: pq! { a_view }, },
|
||||
//]);
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_view_definition () {
|
||||
// FIXME
|
||||
//let parsed: ViewDefinition = pq! {
|
||||
//#[tengri_proc::view(SomeOutput)]
|
||||
//impl SomeView {
|
||||
//#[tengri::view(":view-1")]
|
||||
//fn view_1 (&self) -> impl Content<SomeOutput> + use<'_> {
|
||||
//"view-1"
|
||||
//}
|
||||
//}
|
||||
//};
|
||||
//let written = quote! { #parsed };
|
||||
//assert_eq!(format!("{written}"), format!("{}", quote! {
|
||||
//impl SomeView {
|
||||
//fn view_1 (&self) -> impl Content<SomeOutput> + use<'_> {
|
||||
//"view-1"
|
||||
//}
|
||||
//}
|
||||
///// Generated by [tengri_proc].
|
||||
//impl ::tengri::output::Content<SomeOutput> for SomeView {
|
||||
//fn content (&self) -> impl Render<SomeOutput> {
|
||||
//self.size.of(::tengri::output::View(self, self.config.view))
|
||||
//}
|
||||
//}
|
||||
///// Generated by [tengri_proc].
|
||||
//impl<'a> ::tengri::dsl::ViewContext<'a, SomeOutput> for SomeView {
|
||||
//fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, SomeOutput>> {
|
||||
//match value {
|
||||
//::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(),
|
||||
//_ => panic!("expected Sym(content), got: {value:?}")
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ tengri_input = { optional = true, path = "../input" }
|
|||
tengri_output = { optional = true, path = "../output" }
|
||||
tengri_tui = { optional = true, path = "../tui" }
|
||||
|
||||
[dev-dependencies]
|
||||
tengri_proc = { path = "../proc" }
|
||||
tengri = { path = ".", features = [ "dsl" ] }
|
||||
crossterm = "0.28.1"
|
||||
|
||||
[features]
|
||||
default = [ "input", "output", "tui" ]
|
||||
input = [ "tengri_input" ]
|
||||
|
|
|
|||
|
|
@ -2,3 +2,98 @@
|
|||
#[cfg(feature="input")] pub use ::tengri_input as input;
|
||||
#[cfg(feature="dsl")] pub use ::tengri_dsl as dsl;
|
||||
#[cfg(feature="tui")] pub use ::tengri_tui as tui;
|
||||
|
||||
#[cfg(test)] extern crate tengri_proc;
|
||||
#[cfg(test)] #[test] fn test_subcommand () -> crate::output::Usually<()> {
|
||||
use crate::output::Perhaps;
|
||||
use crate::input::{Command, InputMap, KeyMap, Handle, handle};
|
||||
use crate::dsl::{TryFromDsl, TokenIter};
|
||||
use crate::tui::TuiIn;
|
||||
use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
|
||||
//use crate::input::*;
|
||||
//use crate::dsl::*;
|
||||
struct Test {
|
||||
keys: InputMap<'static, Test, TestCommand, TuiIn, TokenIter<'static>>
|
||||
}
|
||||
handle!(TuiIn: |self: Test, input|if let Some(command) = self.keys.command(self, input) {
|
||||
Ok(Some(true))
|
||||
} else {
|
||||
Ok(None)
|
||||
});
|
||||
#[tengri_proc::command(Test)] impl TestCommand {
|
||||
fn do_thing (state: &mut Test) -> Perhaps<Self> {
|
||||
Ok(None)
|
||||
}
|
||||
fn do_thing_arg (state: &mut Test, arg: usize) -> Perhaps<Self> {
|
||||
Ok(None)
|
||||
}
|
||||
fn do_sub (state: &mut Test, command: TestSubcommand) -> Perhaps<Self> {
|
||||
Ok(command.execute(state)?.map(|command|Self::DoSub { command }))
|
||||
}
|
||||
}
|
||||
#[tengri_proc::command(Test)] impl TestSubcommand {
|
||||
fn do_other_thing (state: &mut Test) -> Perhaps<Self> {
|
||||
Ok(None)
|
||||
}
|
||||
fn do_other_thing_arg (state: &mut Test, arg: usize) -> Perhaps<Self> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
let mut test = Test {
|
||||
keys: InputMap::new("
|
||||
(@a do-thing)
|
||||
(@b do-thing-arg 0)
|
||||
(@c do-sub do-other-thing)
|
||||
(@d do-sub do-other-thing-arg 0)
|
||||
".into())
|
||||
};
|
||||
assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
code: KeyCode::Char('a'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
state: KeyEventState::NONE,
|
||||
})))?);
|
||||
assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
code: KeyCode::Char('b'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
state: KeyEventState::NONE,
|
||||
})))?);
|
||||
assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
state: KeyEventState::NONE,
|
||||
})))?);
|
||||
assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
code: KeyCode::Char('d'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
state: KeyEventState::NONE,
|
||||
})))?);
|
||||
assert_eq!(None, test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
code: KeyCode::Char('z'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
state: KeyEventState::NONE,
|
||||
})))?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_dsl_context () {
|
||||
use crate::dsl::Value;
|
||||
|
||||
struct Test;
|
||||
#[tengri_proc::expose]
|
||||
impl Test {
|
||||
fn some_bool (&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
assert_eq!(Test.get(&Value::Sym(":false")), Some(false));
|
||||
assert_eq!(Test.get(&Value::Sym(":true")), Some(true));
|
||||
assert_eq!(Test.get(&Value::Sym(":some-bool")), Some(true));
|
||||
assert_eq!(Test.get(&Value::Sym(":missing-bool")), None);
|
||||
assert_eq!(Test.get(&Value::Num(0)), Some(false));
|
||||
assert_eq!(Test.get(&Value::Num(1)), Some(true));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ handle!(TuiIn: |self: Example, input|{
|
|||
|
||||
#[tengri_proc::expose]
|
||||
impl Example {
|
||||
fn _todo_u16_stub (&self) -> u16 { todo!() }
|
||||
fn _todo_bool_stub (&self) -> bool { todo!() }
|
||||
fn _todo_usize_stub (&self) -> usize { todo!() }
|
||||
//[bool] => {}
|
||||
//[u16] => {}
|
||||
//[usize] => {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue