parent
c900b6f478
commit
8d80f83795
@ -3,7 +3,7 @@ use super::*;
|
||||
/// An alias, e.g. `name := target`
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
||||
pub(crate) attributes: BTreeSet<Attribute<'src>>,
|
||||
pub(crate) attributes: AttributeSet<'src>,
|
||||
pub(crate) name: Name<'src>,
|
||||
#[serde(
|
||||
bound(serialize = "T: Keyed<'src>"),
|
||||
|
@ -79,7 +79,15 @@ impl<'src> Analyzer<'src> {
|
||||
assignments.push(assignment);
|
||||
}
|
||||
Item::Comment(_) => (),
|
||||
Item::Import { absolute, .. } => {
|
||||
Item::Import {
|
||||
absolute,
|
||||
attributes,
|
||||
..
|
||||
} => {
|
||||
//TODO check attributes for validity
|
||||
|
||||
let _groups = attributes.groups();
|
||||
|
||||
if let Some(absolute) = absolute {
|
||||
stack.push(asts.get(absolute).unwrap());
|
||||
}
|
||||
@ -231,7 +239,7 @@ impl<'src> Analyzer<'src> {
|
||||
fn analyze_alias(alias: &Alias<'src, Name<'src>>) -> CompileResult<'src> {
|
||||
let name = alias.name.lexeme();
|
||||
|
||||
for attribute in &alias.attributes {
|
||||
for attribute in &alias.attributes.inner {
|
||||
if *attribute != Attribute::Private {
|
||||
return Err(alias.name.token.error(AliasInvalidAttribute {
|
||||
alias: name,
|
||||
|
@ -120,6 +120,49 @@ impl<'src> Display for Attribute<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub(crate) struct AttributeSet<'src> {
|
||||
#[serde(flatten)]
|
||||
pub(crate) inner: BTreeSet<Attribute<'src>>,
|
||||
}
|
||||
|
||||
impl<'src> AttributeSet<'src> {
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self {
|
||||
inner: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_map<T>(input: BTreeMap<Attribute<'src>, T>) -> Self {
|
||||
Self {
|
||||
inner: input.into_keys().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_btree_set(self) -> BTreeSet<Attribute<'src>> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn contains(&self, attribute: &Attribute) -> bool {
|
||||
self.inner.contains(attribute)
|
||||
}
|
||||
|
||||
/// Get the names of all Group attributes defined in this attribute set
|
||||
pub(crate) fn groups(&self) -> Vec<&StringLiteral<'src>> {
|
||||
self
|
||||
.inner
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if let Attribute::Group(name) = attr {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -79,6 +79,7 @@ impl Compiler {
|
||||
absolute,
|
||||
optional,
|
||||
path,
|
||||
attributes: _,
|
||||
} => {
|
||||
let import = current
|
||||
.path
|
||||
|
@ -7,6 +7,7 @@ pub(crate) enum Item<'src> {
|
||||
Assignment(Assignment<'src>),
|
||||
Comment(&'src str),
|
||||
Import {
|
||||
attributes: AttributeSet<'src>,
|
||||
absolute: Option<PathBuf>,
|
||||
optional: bool,
|
||||
path: Token<'src>,
|
||||
|
108
src/lib.rs
108
src/lib.rs
@ -21,29 +21,91 @@
|
||||
|
||||
pub(crate) use {
|
||||
crate::{
|
||||
alias::Alias, analyzer::Analyzer, argument_parser::ArgumentParser, assignment::Assignment,
|
||||
assignment_resolver::AssignmentResolver, ast::Ast, attribute::Attribute, binding::Binding,
|
||||
color::Color, color_display::ColorDisplay, command_ext::CommandExt, compilation::Compilation,
|
||||
compile_error::CompileError, compile_error_kind::CompileErrorKind, compiler::Compiler,
|
||||
condition::Condition, conditional_operator::ConditionalOperator, config::Config,
|
||||
config_error::ConfigError, constants::constants, count::Count, delimiter::Delimiter,
|
||||
dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error,
|
||||
evaluator::Evaluator, execution_context::ExecutionContext, expression::Expression,
|
||||
fragment::Fragment, function::Function, interrupt_guard::InterruptGuard,
|
||||
interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyed::Keyed,
|
||||
keyword::Keyword, lexer::Lexer, line::Line, list::List, load_dotenv::load_dotenv,
|
||||
loader::Loader, module_path::ModulePath, name::Name, namepath::Namepath, ordinal::Ordinal,
|
||||
output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind,
|
||||
parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position,
|
||||
positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe,
|
||||
recipe_resolver::RecipeResolver, recipe_signature::RecipeSignature, scope::Scope,
|
||||
search::Search, search_config::SearchConfig, search_error::SearchError, set::Set,
|
||||
setting::Setting, settings::Settings, shebang::Shebang, shell::Shell,
|
||||
show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
|
||||
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
|
||||
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
|
||||
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
|
||||
verbosity::Verbosity, warning::Warning,
|
||||
alias::Alias,
|
||||
analyzer::Analyzer,
|
||||
argument_parser::ArgumentParser,
|
||||
assignment::Assignment,
|
||||
assignment_resolver::AssignmentResolver,
|
||||
ast::Ast,
|
||||
attribute::{Attribute, AttributeSet},
|
||||
binding::Binding,
|
||||
color::Color,
|
||||
color_display::ColorDisplay,
|
||||
command_ext::CommandExt,
|
||||
compilation::Compilation,
|
||||
compile_error::CompileError,
|
||||
compile_error_kind::CompileErrorKind,
|
||||
compiler::Compiler,
|
||||
condition::Condition,
|
||||
conditional_operator::ConditionalOperator,
|
||||
config::Config,
|
||||
config_error::ConfigError,
|
||||
constants::constants,
|
||||
count::Count,
|
||||
delimiter::Delimiter,
|
||||
dependency::Dependency,
|
||||
dump_format::DumpFormat,
|
||||
enclosure::Enclosure,
|
||||
error::Error,
|
||||
evaluator::Evaluator,
|
||||
execution_context::ExecutionContext,
|
||||
expression::Expression,
|
||||
fragment::Fragment,
|
||||
function::Function,
|
||||
interrupt_guard::InterruptGuard,
|
||||
interrupt_handler::InterruptHandler,
|
||||
item::Item,
|
||||
justfile::Justfile,
|
||||
keyed::Keyed,
|
||||
keyword::Keyword,
|
||||
lexer::Lexer,
|
||||
line::Line,
|
||||
list::List,
|
||||
load_dotenv::load_dotenv,
|
||||
loader::Loader,
|
||||
module_path::ModulePath,
|
||||
name::Name,
|
||||
namepath::Namepath,
|
||||
ordinal::Ordinal,
|
||||
output::output,
|
||||
output_error::OutputError,
|
||||
parameter::Parameter,
|
||||
parameter_kind::ParameterKind,
|
||||
parser::Parser,
|
||||
platform::Platform,
|
||||
platform_interface::PlatformInterface,
|
||||
position::Position,
|
||||
positional::Positional,
|
||||
ran::Ran,
|
||||
range_ext::RangeExt,
|
||||
recipe::Recipe,
|
||||
recipe_resolver::RecipeResolver,
|
||||
recipe_signature::RecipeSignature,
|
||||
scope::Scope,
|
||||
search::Search,
|
||||
search_config::SearchConfig,
|
||||
search_error::SearchError,
|
||||
set::Set,
|
||||
setting::Setting,
|
||||
settings::Settings,
|
||||
shebang::Shebang,
|
||||
shell::Shell,
|
||||
show_whitespace::ShowWhitespace,
|
||||
source::Source,
|
||||
string_kind::StringKind,
|
||||
string_literal::StringLiteral,
|
||||
subcommand::Subcommand,
|
||||
suggestion::Suggestion,
|
||||
table::Table,
|
||||
thunk::Thunk,
|
||||
token::Token,
|
||||
token_kind::TokenKind,
|
||||
unresolved_dependency::UnresolvedDependency,
|
||||
unresolved_recipe::UnresolvedRecipe,
|
||||
use_color::UseColor,
|
||||
variables::Variables,
|
||||
verbosity::Verbosity,
|
||||
warning::Warning,
|
||||
},
|
||||
camino::Utf8Path,
|
||||
derivative::Derivative,
|
||||
|
@ -334,7 +334,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
} else if self.next_is(Identifier) {
|
||||
match Keyword::from_lexeme(next.lexeme()) {
|
||||
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||
items.push(Item::Alias(self.parse_alias(BTreeSet::new())?));
|
||||
items.push(Item::Alias(self.parse_alias(AttributeSet::empty())?));
|
||||
}
|
||||
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||
self.presume_keyword(Keyword::Export)?;
|
||||
@ -354,15 +354,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||
|| self.next_are(&[Identifier, QuestionMark]) =>
|
||||
{
|
||||
self.presume_keyword(Keyword::Import)?;
|
||||
let optional = self.accepted(QuestionMark)?;
|
||||
let (path, relative) = self.parse_string_literal_token()?;
|
||||
items.push(Item::Import {
|
||||
absolute: None,
|
||||
optional,
|
||||
path,
|
||||
relative,
|
||||
});
|
||||
items.push(self.parse_import(AttributeSet::empty())?);
|
||||
}
|
||||
Some(Keyword::Mod)
|
||||
if self.next_are(&[Identifier, Identifier, StringToken])
|
||||
@ -408,7 +400,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
items.push(Item::Recipe(self.parse_recipe(
|
||||
doc,
|
||||
false,
|
||||
BTreeSet::new(),
|
||||
AttributeSet::empty(),
|
||||
)?));
|
||||
}
|
||||
}
|
||||
@ -418,7 +410,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
items.push(Item::Recipe(self.parse_recipe(
|
||||
doc,
|
||||
true,
|
||||
BTreeSet::new(),
|
||||
AttributeSet::empty(),
|
||||
)?));
|
||||
} else if let Some(attributes) = self.parse_attributes()? {
|
||||
let next_keyword = Keyword::from_lexeme(self.next()?.lexeme());
|
||||
@ -426,6 +418,13 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||
items.push(Item::Alias(self.parse_alias(attributes)?));
|
||||
}
|
||||
Some(Keyword::Import)
|
||||
if self.next_are(&[Identifier, StringToken])
|
||||
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||
|| self.next_are(&[Identifier, QuestionMark]) =>
|
||||
{
|
||||
items.push(self.parse_import(attributes)?);
|
||||
}
|
||||
_ => {
|
||||
let quiet = self.accepted(At)?;
|
||||
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||
@ -450,10 +449,22 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_import(&mut self, attributes: AttributeSet<'src>) -> CompileResult<'src, Item<'src>> {
|
||||
self.presume_keyword(Keyword::Import)?;
|
||||
let optional = self.accepted(QuestionMark)?;
|
||||
let (path, relative) = self.parse_string_literal_token()?;
|
||||
Ok(Item::Import {
|
||||
absolute: None,
|
||||
attributes,
|
||||
optional,
|
||||
path,
|
||||
relative,
|
||||
})
|
||||
}
|
||||
/// Parse an alias, e.g `alias name := target`
|
||||
fn parse_alias(
|
||||
&mut self,
|
||||
attributes: BTreeSet<Attribute<'src>>,
|
||||
attributes: AttributeSet<'src>,
|
||||
) -> CompileResult<'src, Alias<'src, Name<'src>>> {
|
||||
self.presume_keyword(Keyword::Alias)?;
|
||||
let name = self.parse_name()?;
|
||||
@ -745,7 +756,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
&mut self,
|
||||
doc: Option<&'src str>,
|
||||
quiet: bool,
|
||||
attributes: BTreeSet<Attribute<'src>>,
|
||||
attributes: AttributeSet<'src>,
|
||||
) -> CompileResult<'src, UnresolvedRecipe<'src>> {
|
||||
let name = self.parse_name()?;
|
||||
|
||||
@ -807,7 +818,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
|
||||
Ok(Recipe {
|
||||
shebang: body.first().map_or(false, Line::is_shebang),
|
||||
attributes,
|
||||
attributes: attributes.to_btree_set(),
|
||||
body,
|
||||
dependencies,
|
||||
doc,
|
||||
@ -984,7 +995,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
}
|
||||
|
||||
/// Parse recipe attributes
|
||||
fn parse_attributes(&mut self) -> CompileResult<'src, Option<BTreeSet<Attribute<'src>>>> {
|
||||
fn parse_attributes(&mut self) -> CompileResult<'src, Option<AttributeSet<'src>>> {
|
||||
let mut attributes = BTreeMap::new();
|
||||
|
||||
while self.accepted(BracketL)? {
|
||||
@ -1024,7 +1035,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
if attributes.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(attributes.into_keys().collect()))
|
||||
Ok(Some(AttributeSet::from_map(attributes)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ fn error_from_signal(recipe: &str, line_number: Option<usize>, exit_status: Exit
|
||||
/// A recipe, e.g. `foo: bar baz`
|
||||
#[derive(PartialEq, Debug, Clone, Serialize)]
|
||||
pub(crate) struct Recipe<'src, D = Dependency<'src>> {
|
||||
//TODO make this be attributeset too
|
||||
pub(crate) attributes: BTreeSet<Attribute<'src>>,
|
||||
pub(crate) body: Vec<Line<'src>>,
|
||||
pub(crate) dependencies: Vec<D>,
|
||||
|
Loading…
Reference in New Issue
Block a user