122 lines
3.8 KiB
Rust
122 lines
3.8 KiB
Rust
use crate::ast::{
|
|
Assignment, AstRoot, BinaryOp, Binding, Block, Class, Decl, DeclKind, Expr, ExprKind, Function,
|
|
InteractiveInput, MemberAssignment, Name, Parameter, Program, ReturnStatement, Stmt, StmtKind,
|
|
UnaryOp,
|
|
};
|
|
use crate::lexer::decode_string;
|
|
use crate::{Diagnostic, DiagnosticKind, Span, Token, TokenKind, lex};
|
|
|
|
/// Select the syntax root without implying any execution or REPL session.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum ParseMode {
|
|
Module,
|
|
Expression,
|
|
Interactive,
|
|
}
|
|
|
|
pub fn parse_ast(source: &str, mode: ParseMode) -> Result<AstRoot, Diagnostic> {
|
|
match mode {
|
|
ParseMode::Module => parse(source).map(AstRoot::Module),
|
|
ParseMode::Expression => parse_expression(source).map(AstRoot::Expression),
|
|
ParseMode::Interactive => {
|
|
let tokens = lex(source)?;
|
|
syntax::interactive(&tokens, source)
|
|
.map(AstRoot::Interactive)
|
|
.map_err(|error| diagnostic(error, &tokens, source.len()))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn parse_source_ast(
|
|
source: &crate::SourceUnit,
|
|
mode: ParseMode,
|
|
) -> Result<AstRoot, crate::SourceDiagnostic> {
|
|
parse_ast(source.text(), mode).map_err(|error| source.annotate(error))
|
|
}
|
|
|
|
/// Parse a reader snapshot, retaining its origin and resolved revision on error.
|
|
pub fn parse_source(source: &crate::SourceUnit) -> Result<Program, crate::SourceDiagnostic> {
|
|
parse(source.text()).map_err(|error| source.annotate(error))
|
|
}
|
|
|
|
pub fn parse_source_expression(
|
|
source: &crate::SourceUnit,
|
|
) -> Result<Expr, crate::SourceDiagnostic> {
|
|
parse_expression(source.text()).map_err(|error| source.annotate(error))
|
|
}
|
|
|
|
/// Parse one or more typed function or class declarations. This is a syntax
|
|
/// operation: type names and name references are retained without resolution.
|
|
pub fn parse(source: &str) -> Result<Program, Diagnostic> {
|
|
let tokens = lex(source)?;
|
|
syntax::program(&tokens, source).map_err(|error| diagnostic(error, &tokens, source.len()))
|
|
}
|
|
|
|
/// Parse exactly one complete expression, useful independently of declarations.
|
|
pub fn parse_expression(source: &str) -> Result<Expr, Diagnostic> {
|
|
let tokens = lex(source)?;
|
|
syntax::expression(&tokens, source).map_err(|error| diagnostic(error, &tokens, source.len()))
|
|
}
|
|
|
|
fn diagnostic(error: peg::error::ParseError<usize>, tokens: &[Token], end: usize) -> Diagnostic {
|
|
let span = tokens
|
|
.get(error.location)
|
|
.map_or(Span::new(end, end), |token| token.span);
|
|
let expected: Vec<String> = error.expected.tokens().map(str::to_owned).collect();
|
|
Diagnostic {
|
|
kind: DiagnosticKind::Syntax,
|
|
span,
|
|
message: format!("expected {}", expected.join(" or ")),
|
|
expected,
|
|
}
|
|
}
|
|
|
|
fn binding_statement(binding: Binding) -> Stmt {
|
|
Stmt {
|
|
span: binding.span,
|
|
kind: StmtKind::Binding(binding),
|
|
}
|
|
}
|
|
|
|
fn binary(left: Expr, operation: BinaryOp, right: Expr) -> Expr {
|
|
Expr {
|
|
span: left.span.through(right.span),
|
|
kind: ExprKind::Binary {
|
|
operation,
|
|
left: Box::new(left),
|
|
right: Box::new(right),
|
|
},
|
|
}
|
|
}
|
|
|
|
enum Suffix {
|
|
Member(Name),
|
|
Call(Vec<Expr>, Span),
|
|
}
|
|
|
|
enum ClassMember {
|
|
Field(Parameter),
|
|
Method(Function),
|
|
}
|
|
|
|
fn postfix(expression: Expr, suffix: Suffix) -> Expr {
|
|
match suffix {
|
|
Suffix::Member(name) => Expr {
|
|
span: expression.span.through(name.span),
|
|
kind: ExprKind::Member {
|
|
object: Box::new(expression),
|
|
name,
|
|
},
|
|
},
|
|
Suffix::Call(arguments, end) => Expr {
|
|
span: expression.span.through(end),
|
|
kind: ExprKind::Call {
|
|
callee: Box::new(expression),
|
|
arguments,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
include!("grammar.peg");
|