Files
endorphin-aster-vm/crates/frontend/src/parser.rs
T

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");