summaryrefslogtreecommitdiff
path: root/src/syntax/parsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/parsing.rs')
-rw-r--r--src/syntax/parsing.rs207
1 files changed, 97 insertions, 110 deletions
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index f6d0b629..24bef7ce 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -3,9 +3,8 @@ use super::*;
use Token::*;
-/// Parses source code into a syntax tree given a context.
-pub fn parse(src: &str, ctx: ParseContext) -> (SyntaxTree, Colorization, ErrorMap) {
- Parser::new(src, ctx).parse()
+pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxModel> {
+ Parser::new(start, src, ctx).parse()
}
/// The context for parsing.
@@ -18,64 +17,68 @@ pub struct ParseContext<'a> {
struct Parser<'s> {
src: &'s str,
ctx: ParseContext<'s>,
- colorization: Colorization,
- error_map: ErrorMap,
tokens: Tokens<'s>,
peeked: Option<Option<Spanned<Token<'s>>>>,
position: Position,
last_position: Position,
+ errors: SpanVec<Error>,
+ decorations: SpanVec<Decoration>,
}
impl<'s> Parser<'s> {
- fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
+ fn new(start: Position, src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
Parser {
src,
ctx,
- error_map: ErrorMap::new(),
- colorization: Colorization::new(),
- tokens: Tokens::new(src),
+ tokens: tokenize(start, src),
peeked: None,
position: Position::ZERO,
last_position: Position::ZERO,
+ errors: vec![],
+ decorations: vec![],
}
}
/// The main parsing entrypoint.
- fn parse(mut self) -> (SyntaxTree, Colorization, ErrorMap) {
- let mut tree = SyntaxTree::new();
-
- loop {
- if let Some(spanned) = self.eat() {
- match spanned.v {
- LineComment(_) | BlockComment(_) => {}
-
- Whitespace(newlines) => {
- tree.add(spanned.map_v(if newlines >= 2 {
- Node::Newline
- } else {
- Node::Space
- }));
- }
-
- LeftBracket => {
- if let Some(func) = self.parse_func() {
- tree.add(func);
- }
- }
-
- Star => tree.add(spanned.map_v(Node::ToggleBolder)),
- Underscore => tree.add(spanned.map_v(Node::ToggleItalic)),
- Backtick => tree.add(spanned.map_v(Node::ToggleMonospace)),
- Text(text) => tree.add(spanned.map_v(Node::Text(text.to_owned()))),
-
- _ => self.unexpected(spanned),
+ fn parse(mut self) -> Parsed<SyntaxModel> {
+ let mut model = SyntaxModel::new();
+
+ while let Some(token) = self.eat() {
+ let mut span = token.span;
+ let node = match token.v {
+ LineComment(_) | BlockComment(_) => None,
+ Whitespace(newlines) => Some(if newlines >= 2 {
+ Node::Newline
+ } else {
+ Node::Space
+ }),
+
+ LeftBracket => self.parse_func().map(|spanned| {
+ span = spanned.span;
+ spanned.v
+ }),
+
+ Star => Some(Node::ToggleBolder),
+ Underscore => Some(Node::ToggleItalic),
+ Backtick => Some(Node::ToggleMonospace),
+ Text(text) => Some(Node::Text(text.to_owned())),
+
+ _ => {
+ self.unexpected(token);
+ None
}
- } else {
- break;
+ };
+
+ if let Some(v) = node {
+ model.add(Spanned { v, span });
}
}
- (tree, self.colorization, self.error_map)
+ Parsed {
+ output: model,
+ errors: self.errors,
+ decorations: self.decorations,
+ }
}
/// Parses a function including header and body with the cursor starting
@@ -90,12 +93,55 @@ impl<'s> Parser<'s> {
self.expected_at("closing bracket", self.pos());
}
- let call = self.parse_func_call(header)?;
+ let body = if self.peekv() == Some(LeftBracket) {
+ self.eat();
+
+ let start_index = self.tokens.index();
+ let start_position = self.tokens.pos();
+
+ let found = self.tokens.move_to_closing_bracket();
+
+ let end_index = self.tokens.index();
+ let end_position = self.tokens.pos();
+
+ let body = &self.src[start_index .. end_index];
+
+ self.position = end_position;
+
+ if found {
+ let next = self.eat().map(Spanned::value);
+ debug_assert_eq!(next, Some(RightBracket));
+ } else {
+ self.expected_at("closing bracket", self.pos());
+ }
+
+ Some(Spanned::new(body, Span::new(start_position, end_position)))
+ } else {
+ None
+ };
+
+ let header = header?;
+ let (parser, decoration) = match self.ctx.scope.get_parser(header.name.v.as_str()) {
+ Ok(parser) => (parser, Decoration::ValidFuncName),
+ Err(parser) => {
+ let error = Error::new(format!("unknown function: `{}`", header.name.v));
+ self.errors.push(Spanned::new(error, header.name.span));
+ (parser, Decoration::InvalidFuncName)
+ }
+ };
+
+ self.decorations.push(Spanned::new(decoration, header.name.span));
+
+ let parsed = parser(header, body, self.ctx);
+ self.errors.extend(offset_spans(parsed.errors, start));
+ self.decorations.extend(offset_spans(parsed.decorations, start));
+
+ let node = Node::Model(parsed.output);
let end = self.pos();
let span = Span { start, end };
- Some(Spanned { v: Node::Func(call), span })
+ Some(Spanned { v: node, span })
}
/// Parses a function header including the closing bracket.
@@ -125,7 +171,6 @@ impl<'s> Parser<'s> {
match self.peek() {
Some(Spanned { v: ExprIdent(ident), span }) => {
self.eat();
- self.colorization.replace_last(ColorToken::FuncName);
return Some(Spanned { v: Ident(ident.to_string()), span });
}
other => self.expected_found_or_at("identifier", other, self.pos()),
@@ -144,8 +189,7 @@ impl<'s> Parser<'s> {
match self.peekv() {
Some(RightBracket) | None => break,
_ => match self.parse_arg() {
- Some(Arg::Pos(item)) => args.add_pos(item),
- Some(Arg::Key(pair)) => args.add_key_pair(pair),
+ Some(arg) => args.add(arg),
None => {}
}
}
@@ -165,11 +209,11 @@ impl<'s> Parser<'s> {
let ident = Ident(ident.to_string());
if let Some(Equals) = self.peekv() {
- self.colorization.replace_last(ColorToken::Key);
-
self.eat();
self.skip_whitespace();
+ self.decorations.push(Spanned::new(Decoration::ArgumentKey, span));
+
self.parse_expr().map(|value| {
Arg::Key(Pair {
key: Spanned { v: ident, span },
@@ -251,42 +295,6 @@ impl<'s> Parser<'s> {
Spanned { v: Expression::Object(Object::new()), span }
}
- /// Parse the body of a function invocation.
- fn parse_func_call(&mut self, header: Option<FuncHeader>) -> Option<FuncCall> {
- let body = if self.peekv() == Some(LeftBracket) {
- self.eat();
-
- let start = self.tokens.index();
- let found = self.tokens.move_to_closing_bracket();
- let end = self.tokens.index();
-
- self.last_position = self.position;
- self.position = self.tokens.pos();
-
- let body = &self.src[start .. end];
-
- if found {
- let next = self.eat().map(Spanned::value);
- debug_assert_eq!(next, Some(RightBracket));
- } else {
- self.expected_at("closing bracket", self.pos());
- }
-
- Some(body)
- } else {
- None
- };
-
- let header = header?;
- let parser = self.ctx.scope.get_parser(header.name.v.as_str()).or_else(|| {
- let message = format!("unknown function: `{}`", header.name.v);
- self.error_map.add(message, header.name.span);
- None
- })?;
-
- parser(header, body, self.ctx).ok().map(|f| FuncCall(f))
- }
-
/// Skip all whitespace/comment tokens.
fn skip_whitespace(&mut self) {
self.eat_until(|t|
@@ -296,14 +304,16 @@ impl<'s> Parser<'s> {
/// Add an error about an `thing` which was expected but not found at the
/// given position.
fn expected_at(&mut self, thing: &str, pos: Position) {
- self.error_map.add_at(format!("expected {}", thing), pos);
+ let error = Error::new(format!("expected {}", thing));
+ self.errors.push(Spanned::new(error, Span::at(pos)));
}
/// Add an error about an expected `thing` which was not found, showing
/// what was found instead.
fn expected_found(&mut self, thing: &str, found: Spanned<Token>) {
let message = format!("expected {}, found {}", thing, name(found.v));
- self.error_map.add(message, found.span);
+ let error = Error::new(message);
+ self.errors.push(Spanned::new(error, found.span));
}
/// Add a found-error if `found` is some and a positional error, otherwise.
@@ -321,7 +331,8 @@ impl<'s> Parser<'s> {
/// Add an error about an unexpected token `found`.
fn unexpected(&mut self, found: Spanned<Token>) {
- self.error_map.add(format!("unexpected {}", name(found.v)), found.span);
+ let error = Error::new(format!("unexpected {}", name(found.v)));
+ self.errors.push(Spanned::new(error, found.span));
}
/// Consume tokens until the function returns true and only consume the last
@@ -348,10 +359,6 @@ impl<'s> Parser<'s> {
.unwrap_or_else(|| self.tokens.next());
if let Some(token) = token {
- if let Some(color) = color(token.v) {
- self.colorization.add(color, token.span);
- }
-
self.last_position = self.position;
self.position = token.span.end;
}
@@ -407,23 +414,3 @@ fn name(token: Token) -> &'static str {
Text(_) => "invalid identifier",
}
}
-
-/// The color token corresponding to a token.
-fn color(token: Token) -> Option<ColorToken> {
- Some(match token {
- LineComment(_) | BlockComment(_) => ColorToken::Comment,
- LeftBracket | RightBracket => ColorToken::Bracket,
- LeftParen | RightParen => ColorToken::Paren,
- LeftBrace | RightBrace => ColorToken::Brace,
- Colon => ColorToken::Colon,
- Comma => ColorToken::Comma,
- Equals => ColorToken::Equals,
- ExprIdent(_) => ColorToken::ExprIdent,
- ExprStr(_) => ColorToken::ExprStr,
- ExprNumber(_) => ColorToken::ExprNumber,
- ExprSize(_) => ColorToken::ExprSize,
- ExprBool(_) => ColorToken::ExprBool,
- StarSlash => ColorToken::Invalid,
- _ => return None,
- })
-}