initial changes towards simpler syntax

This commit is contained in:
PgBiel 2024-07-22 13:04:23 -03:00
parent b866e58926
commit c8994d0488
2 changed files with 50 additions and 44 deletions

View File

@ -107,9 +107,38 @@ impl<'a> Annotation<'a> {
self.0.cast_first_match().unwrap_or_default()
}
/// The annotation's arguments. Currently, they are always strings.
pub fn arguments(self) -> impl DoubleEndedIterator<Item = Str<'a>> {
self.0.children().filter_map(Str::from_untyped)
/// The annotation's arguments, which are always either identifiers or
/// strings.
pub fn arguments(self) -> impl DoubleEndedIterator<Item = AnnotationArg<'a>> {
self.0.children().filter_map(AnnotationArg::from_untyped)
}
}
/// An annotation argument, which always corresponds to a simple string, which,
/// however, may be abbreviated and specified as a valid identifier directly.
pub enum AnnotationArg<'a> {
/// An identifier specified directly, without quotes.
Ident(Ident<'a>),
/// A string specified with double quotes.
Str(Str<'a>),
}
impl<'a> AnnotationArg<'a> {
/// Casts an untyped node to an annotation argument, if possible.
pub fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
match node.kind() {
SyntaxKind::Ident => node.cast().map(Self::Ident),
SyntaxKind::Str => node.cast().map(Self::Str),
_ => Option::None,
}
}
/// Gets the text specified for this argument.
pub fn get(self) -> EcoString {
match self {
Self::Ident(ident) => ident.get().clone(),
Self::Str(str) => str.get(),
}
}
}

View File

@ -258,63 +258,40 @@ impl Lexer<'_> {
let name = self.annotation_name(current_start);
subtree.push(self.emit_token(name, current_start));
// Left parenthesis before annotation arguments.
// Optional left parenthesis before annotation arguments.
let current_start = self.s.cursor();
if !self.s.eat_if('(') {
self.s.eat_until(is_newline);
subtree.push(self.emit_error("expected opening paren", current_start));
let has_opening_paren = self.s.eat_if('(');
// Return a single error node until the end of the annotation.
return SyntaxNode::inner(SyntaxKind::Annotation, subtree);
if has_opening_paren {
subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
}
subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
// Annotation arguments:
// Keep reading until we find a right parenthesis or newline. We have
// to check the newline before eating (through '.peek()') to ensure it
// is not considered part of the annotation.
// Keep reading until we find a right parenthesis (if we got a left
// parenthesis) or newline. We have to check the newline before eating
// (through '.peek()') to ensure it is not considered part of the
// annotation.
let mut current_start = self.s.cursor();
let mut expecting_comma = false;
let mut finished = false;
let mut found_closing_paren = false;
while !self.s.at(is_newline) {
let token = match self.s.eat() {
Some(c) if c.is_whitespace() => {
self.s.eat_while(is_inline_whitespace);
SyntaxKind::Space
}
Some('/') if self.s.eat_if('/') => {
let node = self.line_comment_or_annotation(current_start);
if node.kind() == SyntaxKind::Annotation {
self.error("cannot have multiple annotations per line")
} else {
subtree.push(node);
current_start = self.s.cursor();
continue;
}
}
Some('/') if self.s.eat_if('*') => self.block_comment(),
Some(_) if finished => {
Some(_) if found_closing_paren => {
// After we finished specifying arguments, there must only
// be whitespaces until the line ends.
self.s.eat_until(char::is_whitespace);
self.error("expected end of annotation")
}
Some('"') if expecting_comma => {
self.s.eat_until(|c| c == ',' || c == ')' || is_newline(c));
self.error("expected comma")
Some(c) if is_id_start(c) => {
self.s.eat_while(is_id_continue);
SyntaxKind::Ident
}
Some('"') => {
expecting_comma = true;
self.annotation_string()
}
Some(',') if expecting_comma => {
expecting_comma = false;
SyntaxKind::Comma
}
Some(',') => self.error("unexpected comma"),
Some(')') => {
finished = true;
Some('"') => self.annotation_string(),
Some(')') if has_opening_paren => {
found_closing_paren = true;
SyntaxKind::RightParen
}
Some(c) => self.error(eco_format!(
@ -330,7 +307,7 @@ impl Lexer<'_> {
}
// Right parenthesis (covered above)
if !finished {
if has_opening_paren && !found_closing_paren {
subtree.push(self.emit_error("expected closing paren", self.s.cursor()));
}
@ -361,7 +338,7 @@ impl Lexer<'_> {
fn annotation_string(&mut self) -> SyntaxKind {
// TODO: Allow more characters in annotations' strings, perhaps allowing
// newlines somehow.
// Could perhaps use one //! per line so we can break an annotation into
// Could perhaps use one // per line so we can break an annotation into
// multiple lines in a sensible way.
let start = self.s.cursor();
self.s.eat_while(|c| !is_newline(c) && c != '"');