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() self.0.cast_first_match().unwrap_or_default()
} }
/// The annotation's arguments. Currently, they are always strings. /// The annotation's arguments, which are always either identifiers or
pub fn arguments(self) -> impl DoubleEndedIterator<Item = Str<'a>> { /// strings.
self.0.children().filter_map(Str::from_untyped) 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); let name = self.annotation_name(current_start);
subtree.push(self.emit_token(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(); let current_start = self.s.cursor();
if !self.s.eat_if('(') { let has_opening_paren = self.s.eat_if('(');
self.s.eat_until(is_newline);
subtree.push(self.emit_error("expected opening paren", current_start));
// Return a single error node until the end of the annotation. if has_opening_paren {
return SyntaxNode::inner(SyntaxKind::Annotation, subtree); subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
} }
subtree.push(self.emit_token(SyntaxKind::LeftParen, current_start));
// Annotation arguments: // Annotation arguments:
// Keep reading until we find a right parenthesis or newline. We have // Keep reading until we find a right parenthesis (if we got a left
// to check the newline before eating (through '.peek()') to ensure it // parenthesis) or newline. We have to check the newline before eating
// is not considered part of the annotation. // (through '.peek()') to ensure it is not considered part of the
// annotation.
let mut current_start = self.s.cursor(); let mut current_start = self.s.cursor();
let mut expecting_comma = false; let mut found_closing_paren = false;
let mut finished = false;
while !self.s.at(is_newline) { while !self.s.at(is_newline) {
let token = match self.s.eat() { let token = match self.s.eat() {
Some(c) if c.is_whitespace() => { Some(c) if c.is_whitespace() => {
self.s.eat_while(is_inline_whitespace); self.s.eat_while(is_inline_whitespace);
SyntaxKind::Space SyntaxKind::Space
} }
Some('/') if self.s.eat_if('/') => { Some(_) if found_closing_paren => {
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 => {
// After we finished specifying arguments, there must only // After we finished specifying arguments, there must only
// be whitespaces until the line ends. // be whitespaces until the line ends.
self.s.eat_until(char::is_whitespace); self.s.eat_until(char::is_whitespace);
self.error("expected end of annotation") self.error("expected end of annotation")
} }
Some('"') if expecting_comma => { Some(c) if is_id_start(c) => {
self.s.eat_until(|c| c == ',' || c == ')' || is_newline(c)); self.s.eat_while(is_id_continue);
self.error("expected comma") SyntaxKind::Ident
} }
Some('"') => { Some('"') => self.annotation_string(),
expecting_comma = true; Some(')') if has_opening_paren => {
self.annotation_string() found_closing_paren = true;
}
Some(',') if expecting_comma => {
expecting_comma = false;
SyntaxKind::Comma
}
Some(',') => self.error("unexpected comma"),
Some(')') => {
finished = true;
SyntaxKind::RightParen SyntaxKind::RightParen
} }
Some(c) => self.error(eco_format!( Some(c) => self.error(eco_format!(
@ -330,7 +307,7 @@ impl Lexer<'_> {
} }
// Right parenthesis (covered above) // Right parenthesis (covered above)
if !finished { if has_opening_paren && !found_closing_paren {
subtree.push(self.emit_error("expected closing paren", self.s.cursor())); subtree.push(self.emit_error("expected closing paren", self.s.cursor()));
} }
@ -361,7 +338,7 @@ impl Lexer<'_> {
fn annotation_string(&mut self) -> SyntaxKind { fn annotation_string(&mut self) -> SyntaxKind {
// TODO: Allow more characters in annotations' strings, perhaps allowing // TODO: Allow more characters in annotations' strings, perhaps allowing
// newlines somehow. // 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. // multiple lines in a sensible way.
let start = self.s.cursor(); let start = self.s.cursor();
self.s.eat_while(|c| !is_newline(c) && c != '"'); self.s.eat_while(|c| !is_newline(c) && c != '"');