Compare commits
No commits in common. "79a037b7493c877d48fe18da2cb8804bbfa8d0ba" and "4c8938e4ff8b0c8b904fda930d6f024cdc1dd03b" have entirely different histories.
79a037b749
...
4c8938e4ff
5 changed files with 103 additions and 187 deletions
108
src/errors.rs
108
src/errors.rs
|
|
@ -1,108 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use annotate_snippets::AnnotationKind;
|
|
||||||
use annotate_snippets::Level;
|
|
||||||
use annotate_snippets::Renderer;
|
|
||||||
use annotate_snippets::Snippet;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::input::NomoInput;
|
|
||||||
use crate::lexer::ParseError;
|
|
||||||
use crate::parser::AstError;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub struct AstFailure {
|
|
||||||
errors: Vec<AstError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for AstFailure {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str("TODO")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AstFailure {
|
|
||||||
pub(crate) fn from_errors(errors: Vec<AstError>) -> AstFailure {
|
|
||||||
AstFailure { errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_report(&self, source: &str) -> String {
|
|
||||||
let reports = self
|
|
||||||
.errors
|
|
||||||
.iter()
|
|
||||||
.map(|error| {
|
|
||||||
annotate_snippets::Level::ERROR
|
|
||||||
.primary_title(
|
|
||||||
error
|
|
||||||
.message
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or("An error occurred while producing an Ast"),
|
|
||||||
)
|
|
||||||
.element(
|
|
||||||
annotate_snippets::Snippet::source(source).annotation(
|
|
||||||
annotate_snippets::AnnotationKind::Primary
|
|
||||||
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.elements(
|
|
||||||
error
|
|
||||||
.help
|
|
||||||
.as_ref()
|
|
||||||
.map(|help| annotate_snippets::Level::HELP.message(help)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let renderer = annotate_snippets::Renderer::styled()
|
|
||||||
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
|
||||||
renderer.render(&reports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub struct ParseFailure {
|
|
||||||
input: Arc<str>,
|
|
||||||
errors: Vec<ParseError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ParseFailure {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(&self.to_report())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParseFailure {
|
|
||||||
pub(crate) fn from_errors(errors: Vec<ParseError>, input: NomoInput) -> ParseFailure {
|
|
||||||
ParseFailure {
|
|
||||||
input: Arc::from(input.to_string()),
|
|
||||||
errors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_report(&self) -> String {
|
|
||||||
let reports = self
|
|
||||||
.errors
|
|
||||||
.iter()
|
|
||||||
.map(|error| {
|
|
||||||
Level::ERROR
|
|
||||||
.primary_title(
|
|
||||||
error
|
|
||||||
.message
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or("An error occurred while parsing"),
|
|
||||||
)
|
|
||||||
.element(
|
|
||||||
Snippet::source(self.input.as_ref()).annotation(
|
|
||||||
AnnotationKind::Primary
|
|
||||||
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.elements(error.help.as_ref().map(|help| Level::HELP.message(help)))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let renderer =
|
|
||||||
Renderer::styled().decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
|
||||||
renderer.render(&reports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -39,13 +39,60 @@ use winnow::token::take_until;
|
||||||
use winnow::token::take_while;
|
use winnow::token::take_while;
|
||||||
|
|
||||||
use crate::SourceSpan;
|
use crate::SourceSpan;
|
||||||
use crate::errors::ParseFailure;
|
|
||||||
use crate::input::NomoInput;
|
use crate::input::NomoInput;
|
||||||
use crate::resume_after_cut;
|
use crate::resume_after_cut;
|
||||||
|
|
||||||
type Input<'input> = Recoverable<LocatingSlice<NomoInput>, ParseError>;
|
type Input<'input> = Recoverable<LocatingSlice<NomoInput>, ParseError>;
|
||||||
type PResult<'input, T> = Result<T, ParseError>;
|
type PResult<'input, T> = Result<T, ParseError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub struct ParseFailure {
|
||||||
|
input: Arc<str>,
|
||||||
|
errors: Vec<ParseError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ParseFailure {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.to_report())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseFailure {
|
||||||
|
fn from_errors(errors: Vec<ParseError>, input: NomoInput) -> ParseFailure {
|
||||||
|
ParseFailure {
|
||||||
|
input: Arc::from(input.to_string()),
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_report(&self) -> String {
|
||||||
|
let reports = self
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.map(|error| {
|
||||||
|
Level::ERROR
|
||||||
|
.primary_title(
|
||||||
|
error
|
||||||
|
.message
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("An error occurred while parsing"),
|
||||||
|
)
|
||||||
|
.element(
|
||||||
|
Snippet::source(self.input.as_ref()).annotation(
|
||||||
|
AnnotationKind::Primary
|
||||||
|
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.elements(error.help.as_ref().map(|help| Level::HELP.message(help)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let renderer =
|
||||||
|
Renderer::styled().decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
||||||
|
renderer.render(&reports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseError {
|
pub struct ParseError {
|
||||||
pub(crate) message: Option<String>,
|
pub(crate) message: Option<String>,
|
||||||
|
|
|
||||||
78
src/lib.rs
78
src/lib.rs
|
|
@ -26,76 +26,7 @@
|
||||||
feature = "document-features",
|
feature = "document-features",
|
||||||
cfg_attr(doc, doc = ::document_features::document_features!())
|
cfg_attr(doc, doc = ::document_features::document_features!())
|
||||||
)]
|
)]
|
||||||
//!
|
//!
|
||||||
//! ## Syntax
|
|
||||||
//!
|
|
||||||
//! Nomo tries to keep a consistent syntax across its features.
|
|
||||||
//! The main concepts are:
|
|
||||||
//!
|
|
||||||
//! [`nomo`](crate) uses `{{ }}` as its identifiers. Each of them forms a 'block'.
|
|
||||||
//!
|
|
||||||
//! There are two kinds of blocks:
|
|
||||||
//!
|
|
||||||
//! - `{{= <expr> }}` Interpolations
|
|
||||||
//! - `{{ <control> }}` everything else
|
|
||||||
//!
|
|
||||||
//! ### Expressions
|
|
||||||
//!
|
|
||||||
//! An expression in [`nomo`](crate) is anything that produces a value. Notably, control structures
|
|
||||||
//! _do not_ create values. This is different to rust.
|
|
||||||
//!
|
|
||||||
//! So for example this does not work:
|
|
||||||
//!
|
|
||||||
//! ```nomo
|
|
||||||
//! {{= if is_active "Active" else "Inactive" end }}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! If you wish to conditionally output something, you would write:
|
|
||||||
//!
|
|
||||||
//! ```nomo
|
|
||||||
//! {{ if is_active }}
|
|
||||||
//! Active
|
|
||||||
//! {{ else }}
|
|
||||||
//! Inactive
|
|
||||||
//! {{ end }}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! In expressions you can write:
|
|
||||||
//! - Mathematical expressions (`+-*/`)
|
|
||||||
//! - Logical/Binary expressions (`&&`, `||`, `>`, ...)
|
|
||||||
//! - Literals (`12`, `292.21`, `"Hello"`)
|
|
||||||
//!
|
|
||||||
//! ### Interpolations
|
|
||||||
//!
|
|
||||||
//! Interpolations is how one prints out data. A [`NomoValue`] can be printed if it is a:
|
|
||||||
//!
|
|
||||||
//! - String
|
|
||||||
//! - Integer/SignedInteger
|
|
||||||
//! - Bool
|
|
||||||
//! - Float
|
|
||||||
//!
|
|
||||||
//! All other values will result in an error.
|
|
||||||
//!
|
|
||||||
//! ### Control Structures
|
|
||||||
//!
|
|
||||||
//! [`Nomo`](crate) supports several control structures:
|
|
||||||
//!
|
|
||||||
//! **Conditions `if/else`**:
|
|
||||||
//!
|
|
||||||
//! ```nomo
|
|
||||||
//! {{ if <expr> }}
|
|
||||||
//! {{ else if <expr> }}
|
|
||||||
//! {{ else }}
|
|
||||||
//! {{ end }}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! **Loops `for .. in`**
|
|
||||||
//!
|
|
||||||
//! ```nomo
|
|
||||||
//! {{ for <identifier> in <expr> }}
|
|
||||||
//! {{ else }}
|
|
||||||
//! {{ end }}
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -136,9 +67,6 @@ unstable_pub!(
|
||||||
mod parser
|
mod parser
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Nomo Functions
|
|
||||||
pub mod errors;
|
|
||||||
|
|
||||||
/// Nomo Functions
|
/// Nomo Functions
|
||||||
pub mod functions;
|
pub mod functions;
|
||||||
/// Input for nomo
|
/// Input for nomo
|
||||||
|
|
@ -153,13 +81,13 @@ pub enum NomoError {
|
||||||
ParseError {
|
ParseError {
|
||||||
#[from]
|
#[from]
|
||||||
#[expect(missing_docs)]
|
#[expect(missing_docs)]
|
||||||
source: errors::ParseFailure,
|
source: lexer::ParseFailure,
|
||||||
},
|
},
|
||||||
/// Invalid Template
|
/// Invalid Template
|
||||||
AstError {
|
AstError {
|
||||||
#[from]
|
#[from]
|
||||||
#[expect(missing_docs)]
|
#[expect(missing_docs)]
|
||||||
source: errors::AstFailure,
|
source: parser::AstFailure,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An error occurred while evaluating
|
/// An error occurred while evaluating
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use thiserror::Error;
|
||||||
use winnow::Parser;
|
use winnow::Parser;
|
||||||
use winnow::RecoverableParser;
|
use winnow::RecoverableParser;
|
||||||
use winnow::combinator::Infix::Left;
|
use winnow::combinator::Infix::Left;
|
||||||
|
|
@ -27,7 +28,6 @@ use winnow::stream::TokenSlice;
|
||||||
use winnow::token::any;
|
use winnow::token::any;
|
||||||
|
|
||||||
use crate::SourceSpan;
|
use crate::SourceSpan;
|
||||||
use crate::errors::AstFailure;
|
|
||||||
use crate::lexer::TemplateToken;
|
use crate::lexer::TemplateToken;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::lexer::TokenOperator;
|
use crate::lexer::TokenOperator;
|
||||||
|
|
@ -151,6 +151,55 @@ impl ParserError<Input<'_>> for AstError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub struct AstFailure {
|
||||||
|
errors: Vec<AstError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for AstFailure {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("TODO")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstFailure {
|
||||||
|
fn from_errors(errors: Vec<AstError>) -> AstFailure {
|
||||||
|
AstFailure { errors }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_report(&self, source: &str) -> String {
|
||||||
|
let reports = self
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.map(|error| {
|
||||||
|
annotate_snippets::Level::ERROR
|
||||||
|
.primary_title(
|
||||||
|
error
|
||||||
|
.message
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("An error occurred while producing an Ast"),
|
||||||
|
)
|
||||||
|
.element(
|
||||||
|
annotate_snippets::Snippet::source(source).annotation(
|
||||||
|
annotate_snippets::AnnotationKind::Primary
|
||||||
|
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.elements(
|
||||||
|
error
|
||||||
|
.help
|
||||||
|
.as_ref()
|
||||||
|
.map(|help| annotate_snippets::Level::HELP.message(help)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let renderer = annotate_snippets::Renderer::styled()
|
||||||
|
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
||||||
|
renderer.render(&reports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Input<'input> = Recoverable<TokenSlice<'input, TemplateToken>, AstError>;
|
type Input<'input> = Recoverable<TokenSlice<'input, TemplateToken>, AstError>;
|
||||||
|
|
||||||
impl<'input> Parser<Input<'input>, TemplateToken, AstError> for TokenKind {
|
impl<'input> Parser<Input<'input>, TemplateToken, AstError> for TokenKind {
|
||||||
|
|
|
||||||
|
|
@ -246,12 +246,12 @@ impl NomoValue {
|
||||||
pub(crate) fn try_to_string(&self) -> Option<String> {
|
pub(crate) fn try_to_string(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
NomoValue::String { value } => Some(value.to_string()),
|
NomoValue::String { value } => Some(value.to_string()),
|
||||||
|
NomoValue::Array { .. } => None,
|
||||||
NomoValue::Bool { value } => Some(value.to_string()),
|
NomoValue::Bool { value } => Some(value.to_string()),
|
||||||
|
NomoValue::Object { .. } => None,
|
||||||
NomoValue::Integer { value } => Some(value.to_string()),
|
NomoValue::Integer { value } => Some(value.to_string()),
|
||||||
NomoValue::SignedInteger { value } => Some(value.to_string()),
|
NomoValue::SignedInteger { value } => Some(value.to_string()),
|
||||||
NomoValue::Float { value } => Some(value.to_string()),
|
NomoValue::Float { value } => Some(value.to_string()),
|
||||||
NomoValue::Array { .. } => None,
|
|
||||||
NomoValue::Object { .. } => None,
|
|
||||||
NomoValue::Iterator { .. } => None,
|
NomoValue::Iterator { .. } => None,
|
||||||
NomoValue::Undefined => None,
|
NomoValue::Undefined => None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue