Add base statement parsing
Assign statements and standalone expression statements are added, plus
some tests.
Additionally, starting tokens are implemented using lazy_static!{} so
that they can be mixed and matched.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -95,6 +95,7 @@ pub enum BaseExprKind {
|
|||||||
List(Vec<Expr>),
|
List(Vec<Expr>),
|
||||||
Object(Vec<(Expr, Expr)>),
|
Object(Vec<(Expr, Expr)>),
|
||||||
Tuple(Vec<Expr>),
|
Tuple(Vec<Expr>),
|
||||||
|
Block(Vec<Stmt>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Clone, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use crate::syn::{ast::*, error::*, lexer::Lexer, op::*, span::*, token::*};
|
use crate::syn::{ast::*, error::*, lexer::Lexer, op::*, span::*, token::*};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use std::{convert::TryFrom, mem};
|
use std::{convert::TryFrom, mem};
|
||||||
|
|
||||||
const BASE_EXPR_START: &[TokenKind] = &[
|
lazy_static! {
|
||||||
|
static ref BASE_EXPR_START: Vec<TokenKind> = vec![
|
||||||
TokenKind::Ident,
|
TokenKind::Ident,
|
||||||
TokenKind::Num,
|
TokenKind::Num,
|
||||||
TokenKind::Str,
|
TokenKind::Str,
|
||||||
@@ -11,8 +13,16 @@ const BASE_EXPR_START: &[TokenKind] = &[
|
|||||||
TokenKind::LBrace,
|
TokenKind::LBrace,
|
||||||
TokenKind::ObjBrace,
|
TokenKind::ObjBrace,
|
||||||
];
|
];
|
||||||
|
static ref UN_EXPR_START: Vec<TokenKind> =
|
||||||
|
vec![TokenKind::Plus, TokenKind::Minus, TokenKind::Bang];
|
||||||
|
static ref EXPR_START: Vec<TokenKind> = {
|
||||||
|
let mut start = BASE_EXPR_START.clone();
|
||||||
|
start.extend(UN_EXPR_START.iter());
|
||||||
|
start
|
||||||
|
};
|
||||||
|
|
||||||
const UN_EXPR_START: &[TokenKind] = &[TokenKind::Plus, TokenKind::Minus, TokenKind::Bang];
|
static ref EOL: Vec<TokenKind> = vec![TokenKind::Eol, TokenKind::Newline];
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Parser<'t> {
|
pub struct Parser<'t> {
|
||||||
lexer: Lexer<'t>,
|
lexer: Lexer<'t>,
|
||||||
@@ -66,9 +76,54 @@ impl<'t> Parser<'t> {
|
|||||||
// Statement parsing
|
// Statement parsing
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
impl<'t> Parser<'t> {
|
impl<'t> Parser<'t> {
|
||||||
pub fn next_stmt(&mut self) -> Result<Stmt> {
|
pub fn next_stmt(&mut self) -> Result<Option<Stmt>> {
|
||||||
|
// skip any leading EOL tokens
|
||||||
|
while self.match_any_token_kind(&EOL)?.is_some() {}
|
||||||
|
|
||||||
|
if self.is_eof() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_any_token_kind(&EXPR_START) {
|
||||||
|
let expr = self.next_expr()?;
|
||||||
|
if self.match_token_kind(TokenKind::Eq)?.is_some() {
|
||||||
|
let rhs = self.next_expr()?;
|
||||||
|
self.expect_eol_or_eof("end-of-line after assignment")?;
|
||||||
|
let span = expr.span().union(rhs.span());
|
||||||
|
Ok(Some(Stmt::Assign(AssignStmt { lhs: expr, rhs, span, })))
|
||||||
|
} else {
|
||||||
|
// TODO catch EOF
|
||||||
|
self.expect_eol_or_eof("end-of-line after expression")?;
|
||||||
|
Ok(Some(Stmt::Expr(expr)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a brace-enclosed list of statements.
|
||||||
|
pub fn next_block(&mut self) -> Result<Vec<Stmt>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a list of statements.
|
||||||
|
pub fn next_body(&mut self) -> Result<Vec<Stmt>> {
|
||||||
|
let mut body = Vec::new();
|
||||||
|
|
||||||
|
if self.is_token_kind(TokenKind::RBrace) {
|
||||||
|
// end of body token
|
||||||
|
return Ok(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(stmt) = self.next_stmt()? {
|
||||||
|
body.push(stmt);
|
||||||
|
if self.is_token_kind(TokenKind::RBrace) {
|
||||||
|
// end of body token
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -79,7 +134,7 @@ macro_rules! bin_expr {
|
|||||||
($name:ident, $op_tokens:expr, $next:ident) => {
|
($name:ident, $op_tokens:expr, $next:ident) => {
|
||||||
fn $name(&mut self) -> Result<Expr> {
|
fn $name(&mut self) -> Result<Expr> {
|
||||||
let lhs = self.$next()?;
|
let lhs = self.$next()?;
|
||||||
if let Some(token) = self.match_token_where(|t| $op_tokens.contains(&t.kind()))? {
|
if let Some(token) = self.match_any_token_kind($op_tokens)? {
|
||||||
let op = BinOp::from(token);
|
let op = BinOp::from(token);
|
||||||
let rhs = self.$name()?;
|
let rhs = self.$name()?;
|
||||||
let span = lhs.span().union(rhs.span());
|
let span = lhs.span().union(rhs.span());
|
||||||
@@ -122,7 +177,7 @@ impl<'t> Parser<'t> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn next_un_expr(&mut self) -> Result<Expr> {
|
fn next_un_expr(&mut self) -> Result<Expr> {
|
||||||
if let Some(un_op) = self.match_token_where(|t| UN_EXPR_START.contains(&t.kind()))? {
|
if let Some(un_op) = self.match_any_token_kind(&UN_EXPR_START)? {
|
||||||
let start = un_op.span();
|
let start = un_op.span();
|
||||||
let expr = self.next_un_expr()?;
|
let expr = self.next_un_expr()?;
|
||||||
let end = expr.span();
|
let end = expr.span();
|
||||||
@@ -140,7 +195,7 @@ impl<'t> Parser<'t> {
|
|||||||
|
|
||||||
fn next_base_expr(&mut self) -> Result<Expr> {
|
fn next_base_expr(&mut self) -> Result<Expr> {
|
||||||
let token =
|
let token =
|
||||||
self.expect_token_where(|t| BASE_EXPR_START.contains(&t.kind()), "base expression")?;
|
self.expect_any_token_kind(&BASE_EXPR_START, "base expression")?;
|
||||||
let expr: Expr = match token.kind() {
|
let expr: Expr = match token.kind() {
|
||||||
TokenKind::Ident => BaseExpr {
|
TokenKind::Ident => BaseExpr {
|
||||||
kind: BaseExprKind::Ident,
|
kind: BaseExprKind::Ident,
|
||||||
@@ -179,6 +234,7 @@ impl<'t> Parser<'t> {
|
|||||||
TokenKind::ObjBrace => {
|
TokenKind::ObjBrace => {
|
||||||
let prev_skip = self.set_skip_newlines(true)?;
|
let prev_skip = self.set_skip_newlines(true)?;
|
||||||
let object = self.next_obj_list()?;
|
let object = self.next_obj_list()?;
|
||||||
|
self.set_skip_newlines(prev_skip)?;
|
||||||
let end_token =
|
let end_token =
|
||||||
self.expect_token_kind(TokenKind::RBrace, "end of object (right curly brace)")?;
|
self.expect_token_kind(TokenKind::RBrace, "end of object (right curly brace)")?;
|
||||||
let span = token.span().union(end_token.span());
|
let span = token.span().union(end_token.span());
|
||||||
@@ -303,6 +359,14 @@ impl<'t> Parser<'t> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_any_token_kind(&mut self, kinds: &[TokenKind]) -> Result<Option<Token>> {
|
||||||
|
if self.is_any_token_kind(kinds) {
|
||||||
|
self.adv_token()
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_token_match<P>(&self, pred: P) -> bool
|
fn is_token_match<P>(&self, pred: P) -> bool
|
||||||
where
|
where
|
||||||
P: Fn(Token) -> bool,
|
P: Fn(Token) -> bool,
|
||||||
@@ -317,6 +381,10 @@ impl<'t> Parser<'t> {
|
|||||||
self.is_token_match(|t| t.kind() == kind)
|
self.is_token_match(|t| t.kind() == kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_any_token_kind(&self, kinds: &[TokenKind]) -> bool {
|
||||||
|
self.is_token_match(|t| kinds.contains(&t.kind()))
|
||||||
|
}
|
||||||
|
|
||||||
fn expect_token_where<P>(&mut self, pred: P, expected: impl ToString) -> Result<Token>
|
fn expect_token_where<P>(&mut self, pred: P, expected: impl ToString) -> Result<Token>
|
||||||
where
|
where
|
||||||
P: Fn(Token) -> bool,
|
P: Fn(Token) -> bool,
|
||||||
@@ -335,6 +403,18 @@ impl<'t> Parser<'t> {
|
|||||||
fn expect_token_kind(&mut self, kind: TokenKind, expected: impl ToString) -> Result<Token> {
|
fn expect_token_kind(&mut self, kind: TokenKind, expected: impl ToString) -> Result<Token> {
|
||||||
self.expect_token_where(|t| t.kind() == kind, expected)
|
self.expect_token_where(|t| t.kind() == kind, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_any_token_kind(&mut self, kinds: &[TokenKind], expected: impl ToString) -> Result<Token> {
|
||||||
|
self.expect_token_where(|t| kinds.contains(&t.kind()), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_eol_or_eof(&mut self, expected: impl ToString) -> Result<Option<Token>> {
|
||||||
|
if self.curr_token.is_none() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
self.expect_any_token_kind(&EOL, expected).map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t> Spanned for Parser<'t> {
|
impl<'t> Spanned for Parser<'t> {
|
||||||
@@ -446,6 +526,10 @@ mod test {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Expr testing
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
fn bin_expr(lhs: Expr, op: BinOp, rhs: Expr) -> Expr {
|
fn bin_expr(lhs: Expr, op: BinOp, rhs: Expr) -> Expr {
|
||||||
Expr::Bin(
|
Expr::Bin(
|
||||||
BinExpr {
|
BinExpr {
|
||||||
@@ -779,4 +863,94 @@ mod test {
|
|||||||
)]))
|
)]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stmt testing
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
fn assign_stmt(lhs: Expr, rhs: Expr) -> Stmt {
|
||||||
|
Stmt::Assign(
|
||||||
|
AssignStmt {
|
||||||
|
lhs,
|
||||||
|
rhs,
|
||||||
|
span: Default::default(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assign_stmt() {
|
||||||
|
test_parser!(
|
||||||
|
Parser::try_from(
|
||||||
|
r"
|
||||||
|
a = 1
|
||||||
|
|
||||||
|
b = :sym
|
||||||
|
"
|
||||||
|
).unwrap(),
|
||||||
|
next_stmt,
|
||||||
|
Some(assign_stmt(
|
||||||
|
base_expr(BaseExprKind::Ident),
|
||||||
|
base_expr(BaseExprKind::Num),
|
||||||
|
)),
|
||||||
|
next_stmt,
|
||||||
|
Some(assign_stmt(
|
||||||
|
base_expr(BaseExprKind::Ident),
|
||||||
|
base_expr(BaseExprKind::Sym),
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expr_stmt() {
|
||||||
|
test_parser!(
|
||||||
|
Parser::try_from(
|
||||||
|
r"
|
||||||
|
a
|
||||||
|
'b'
|
||||||
|
:sym
|
||||||
|
1
|
||||||
|
"
|
||||||
|
).unwrap(),
|
||||||
|
next_stmt,
|
||||||
|
Some(Stmt::Expr (
|
||||||
|
base_expr(BaseExprKind::Ident),
|
||||||
|
)),
|
||||||
|
next_stmt,
|
||||||
|
Some(Stmt::Expr (
|
||||||
|
base_expr(BaseExprKind::Str),
|
||||||
|
)),
|
||||||
|
next_stmt,
|
||||||
|
Some(Stmt::Expr (
|
||||||
|
base_expr(BaseExprKind::Sym),
|
||||||
|
)),
|
||||||
|
next_stmt,
|
||||||
|
Some(Stmt::Expr (
|
||||||
|
base_expr(BaseExprKind::Num),
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
test_parser!(
|
||||||
|
Parser::try_from(
|
||||||
|
r"
|
||||||
|
(a
|
||||||
|
+
|
||||||
|
1
|
||||||
|
*
|
||||||
|
3)"
|
||||||
|
).unwrap(),
|
||||||
|
next_stmt,
|
||||||
|
Some(Stmt::Expr(
|
||||||
|
bin_expr(
|
||||||
|
base_expr(BaseExprKind::Ident),
|
||||||
|
BinOp::Plus,
|
||||||
|
bin_expr(
|
||||||
|
base_expr(BaseExprKind::Num),
|
||||||
|
BinOp::Times,
|
||||||
|
base_expr(BaseExprKind::Num)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user