add support for if statements
This commit is contained in:
parent
05e73aeb21
commit
cb98c78b37
5 changed files with 119 additions and 19 deletions
|
@ -79,6 +79,7 @@ impl Parser {
|
|||
let token = self.lexer.peek_or_err()?;
|
||||
match token.kind {
|
||||
token::Kind::DependKeyword => self.parse_depend_stmt(),
|
||||
token::Kind::IfKeyword => self.parse_if_stmt(),
|
||||
token::Kind::SetKeyword => self.parse_set_stmt(),
|
||||
token::Kind::SourceKeyword => self.parse_source_stmt(),
|
||||
token::Kind::TargetKeyword => self.parse_target_stmt(),
|
||||
|
@ -88,6 +89,28 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// BlockStatement
|
||||
/// : "{" [ StatementList ] "}"
|
||||
///
|
||||
/// StatementList
|
||||
/// : Statement [ StatementList ]
|
||||
/// ```
|
||||
fn parse_block_stmt(&mut self) -> Result<tree::Node, Error> {
|
||||
let mut nodes = Vec::new();
|
||||
self.lexer.expect_kind(token::Kind::OBrace)?;
|
||||
while let Some(result) = self.lexer.peek() {
|
||||
match result?.kind {
|
||||
token::Kind::CBrace => {
|
||||
self.lexer.next();
|
||||
break;
|
||||
}
|
||||
_ => nodes.push(self.parse_stmt()?),
|
||||
}
|
||||
}
|
||||
Ok(tree::Node::Block(nodes))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// TargetStatement
|
||||
/// : "target" Expression BlockStatement
|
||||
|
@ -99,24 +122,13 @@ impl Parser {
|
|||
|
||||
self.lexer.expect_kind(token::Kind::TargetKeyword)?;
|
||||
let name_token = self.lexer.expect_kind(token::Kind::Ident)?;
|
||||
self.lexer.expect_kind(token::Kind::OBrace)?;
|
||||
|
||||
let mut children = Vec::new();
|
||||
|
||||
while let Some(result) = self.lexer.peek() {
|
||||
match result?.kind {
|
||||
token::Kind::CBrace => {
|
||||
self.lexer.next();
|
||||
break;
|
||||
}
|
||||
_ => children.push(self.parse_stmt()?),
|
||||
}
|
||||
}
|
||||
let children = self.parse_block_stmt()?;
|
||||
|
||||
self.scope.pop();
|
||||
Ok(tree::Node::Target {
|
||||
name: Box::new(tree::Node::Ident(name_token.raw)),
|
||||
content: children,
|
||||
content: Box::new(children),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -133,6 +145,29 @@ impl Parser {
|
|||
Ok(tree::Node::DepList(Box::new(rvalue)))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// IfStatement
|
||||
/// : "if" "(" Expression ")" BlockStatement [ "else" BlockStatement ]
|
||||
/// ```
|
||||
fn parse_if_stmt(&mut self) -> Result<tree::Node, Error> {
|
||||
self.lexer.expect_kind(token::Kind::IfKeyword)?;
|
||||
self.lexer.expect_kind(token::Kind::OParen)?;
|
||||
let condition = self.parse_expr(&[token::Kind::CParen])?;
|
||||
let then_block = self.parse_block_stmt()?;
|
||||
let mut else_block = None;
|
||||
if let Some(Ok(token)) = self.lexer.peek() {
|
||||
if token.kind == token::Kind::ElseKeyword {
|
||||
self.lexer.next();
|
||||
else_block = Some(Box::new(self.parse_block_stmt()?));
|
||||
}
|
||||
}
|
||||
Ok(tree::Node::IfStmt {
|
||||
condition: Box::new(condition),
|
||||
then_block: Box::new(then_block),
|
||||
else_block,
|
||||
})
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// SetStatement
|
||||
/// : "set" AssignmentExpression ";"
|
||||
|
@ -216,6 +251,13 @@ impl Parser {
|
|||
expr
|
||||
}
|
||||
|
||||
/// Parse an assignment expression.
|
||||
/// This is no different to parsing any other binary expression, with the
|
||||
/// difference being that assignment operators are right associative.
|
||||
/// Therefore, we need a separate function for this. Other than that,
|
||||
/// this method is no different to `parse_binary_expr_or_higher()` (it even
|
||||
/// returns the same kind of tree node).
|
||||
///
|
||||
/// ```notrust
|
||||
/// AssignmentExpression
|
||||
/// : PrimaryExpression AssignmentOperator Expression
|
||||
|
@ -228,6 +270,11 @@ impl Parser {
|
|||
&mut self,
|
||||
terminators: &[token::Kind],
|
||||
) -> Result<tree::Node, Error> {
|
||||
// we speculate on this being an assignment expression, so we need to
|
||||
// be able to undo our work in case this speculation doesn't hold true
|
||||
// so parse_binary_expr_or_higher() can do its thing
|
||||
let bookmark = self.lexer.save();
|
||||
|
||||
let lhs = self.parse_primary_expr()?;
|
||||
if let Some(Ok(token)) = self.lexer.peek() {
|
||||
if token.kind.is_assignment_op() {
|
||||
|
@ -239,6 +286,10 @@ impl Parser {
|
|||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs),
|
||||
});
|
||||
} else if token.kind.binary_op_precedence().is_some() {
|
||||
// shoot, this wasn't an assignment, all of our work was useless
|
||||
self.lexer.restore(bookmark);
|
||||
return self.parse_binary_expr_or_higher(terminators);
|
||||
} else {
|
||||
self.lexer.expect_kinds(terminators)?;
|
||||
}
|
||||
|
@ -246,7 +297,8 @@ impl Parser {
|
|||
Ok(lhs)
|
||||
}
|
||||
|
||||
/// Binary expressions are generally left associative.
|
||||
/// Binary expressions are generally left associative (except for assignments,
|
||||
/// which are handled separately in `parse_assignment_expr_or_higher()`).
|
||||
/// However, things get a little more tricky when taking the fact that there
|
||||
/// are 9 different levels of precedence into account.
|
||||
///
|
||||
|
@ -255,6 +307,7 @@ impl Parser {
|
|||
/// : Expression BinaryOperator Expression
|
||||
///
|
||||
/// BinaryOperator
|
||||
/// : "||" | "&&" | "==" | "!=" | "<" | "<=" | ">" | ">="
|
||||
/// : "|" | "^" | "&" | "<<" | ">>" | "+" | "-" | "*" | "/" | "%"
|
||||
/// ```
|
||||
fn parse_binary_expr_or_higher(
|
||||
|
|
|
@ -14,6 +14,7 @@ pub enum Node {
|
|||
Int(i128),
|
||||
String(String),
|
||||
Array(Vec<Node>),
|
||||
Block(Vec<Node>),
|
||||
ArrayExpr {
|
||||
// array access
|
||||
array: Box<Node>,
|
||||
|
@ -24,6 +25,11 @@ pub enum Node {
|
|||
func: Box<Node>,
|
||||
params: Vec<Node>,
|
||||
},
|
||||
IfStmt {
|
||||
condition: Box<Node>,
|
||||
then_block: Box<Node>,
|
||||
else_block: Option<Box<Node>>,
|
||||
},
|
||||
UnaryExpr {
|
||||
op: Operator,
|
||||
node: Box<Node>,
|
||||
|
@ -40,7 +46,7 @@ pub enum Node {
|
|||
},
|
||||
Target {
|
||||
name: Box<Node>,
|
||||
content: Vec<Node>,
|
||||
content: Box<Node>,
|
||||
},
|
||||
File {
|
||||
name: String,
|
||||
|
@ -100,6 +106,11 @@ impl Node {
|
|||
node.visit(cb, depth);
|
||||
}
|
||||
}
|
||||
Node::Block(statements) => {
|
||||
for node in statements {
|
||||
node.visit(cb, depth);
|
||||
}
|
||||
}
|
||||
Node::ArrayExpr { array, index } => {
|
||||
array.visit(cb, depth);
|
||||
index.visit(cb, depth);
|
||||
|
@ -110,6 +121,13 @@ impl Node {
|
|||
p.visit(cb, depth);
|
||||
}
|
||||
}
|
||||
Node::IfStmt { condition, then_block, else_block} => {
|
||||
condition.visit(cb, depth);
|
||||
then_block.visit(cb, depth);
|
||||
if let Some(eb) = else_block {
|
||||
eb.visit(cb, depth);
|
||||
}
|
||||
}
|
||||
Node::UnaryExpr { op, node } => node.visit(cb, depth),
|
||||
Node::BinaryExpr { op, lhs, rhs } => {
|
||||
lhs.visit(cb, depth);
|
||||
|
@ -122,9 +140,7 @@ impl Node {
|
|||
}
|
||||
Node::Target { name, content } => {
|
||||
name.visit(cb, depth);
|
||||
for n in content {
|
||||
n.visit(cb, depth);
|
||||
}
|
||||
content.visit(cb, depth);
|
||||
}
|
||||
Node::File { name, content } => {
|
||||
for n in content {
|
||||
|
@ -149,11 +165,13 @@ impl fmt::Display for Node {
|
|||
tmp.as_str()
|
||||
}
|
||||
Node::String(s) => s.as_str(),
|
||||
Node::Block(_) => "<block>",
|
||||
Node::DepList(_) => "depend",
|
||||
Node::SourceList(_) => "source",
|
||||
Node::Array(_) => "<array>",
|
||||
Node::ArrayExpr { array, index } => "<array-access>",
|
||||
Node::CallExpr { func, params } => "<call>",
|
||||
Node::IfStmt { condition, then_block, else_block } => "<if>",
|
||||
Node::UnaryExpr { op, node } => op.raw(),
|
||||
Node::BinaryExpr { op, lhs, rhs } => op.raw(),
|
||||
Node::TypeExpr(_) => "type",
|
||||
|
|
|
@ -15,6 +15,12 @@ pub struct Lexer {
|
|||
token_col: usize,
|
||||
}
|
||||
|
||||
/// A simple bookmark for restoring the lexer's state in case of a failed lookahead.
|
||||
pub struct Bookmark {
|
||||
/// Index within `Lexer.history`
|
||||
index: usize,
|
||||
}
|
||||
|
||||
static NUMERALS: [char; 16] = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
];
|
||||
|
@ -29,8 +35,10 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap {
|
|||
KeywordMap { raw, kind }
|
||||
}
|
||||
|
||||
static KEYWORDS: [KeywordMap; 6] = [
|
||||
static KEYWORDS: [KeywordMap; 8] = [
|
||||
kw("else", token::Kind::ElseKeyword),
|
||||
kw("depend", token::Kind::DependKeyword),
|
||||
kw("if", token::Kind::IfKeyword),
|
||||
kw("include", token::Kind::IncludeKeyword),
|
||||
kw("set", token::Kind::SetKeyword),
|
||||
kw("source", token::Kind::SourceKeyword),
|
||||
|
@ -132,6 +140,18 @@ impl Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Save the lexer's state to perform a speculative lookahead.
|
||||
pub fn save(&mut self) -> Bookmark {
|
||||
Bookmark {
|
||||
index: self.history.len() - self.offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore the lexer's state in case of a failed lookahead.
|
||||
pub fn restore(&mut self, bookmark: Bookmark) {
|
||||
self.offset = self.history.len() - bookmark.index;
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<&Token> {
|
||||
if self.history.len() > 0 {
|
||||
Some(&self.history[self.history.len() - self.offset - 1])
|
||||
|
|
|
@ -70,7 +70,9 @@ pub enum Kind {
|
|||
Percent,
|
||||
PercentEq,
|
||||
|
||||
ElseKeyword,
|
||||
DependKeyword,
|
||||
IfKeyword,
|
||||
IncludeKeyword,
|
||||
TargetKeyword,
|
||||
SetKeyword,
|
||||
|
@ -212,7 +214,9 @@ impl fmt::Display for Kind {
|
|||
Kind::Percent => "percent",
|
||||
Kind::PercentEq => "percenteq",
|
||||
|
||||
Kind::ElseKeyword => "keyword",
|
||||
Kind::DependKeyword => "keyword",
|
||||
Kind::IfKeyword => "keyword",
|
||||
Kind::IncludeKeyword => "keyword",
|
||||
Kind::SetKeyword => "keyword",
|
||||
Kind::SourceKeyword => "keyword",
|
||||
|
|
|
@ -9,6 +9,11 @@ set BUILD_PREFIX = "build";
|
|||
# a target is a single component.
|
||||
# targets can depend on other targets.
|
||||
target kern {
|
||||
if (2 > 3) {
|
||||
print("2 > 3");
|
||||
} else {
|
||||
print("2 <= 3");
|
||||
}
|
||||
|
||||
owo = 1 + 2 * 3 * 4 - 5;
|
||||
|
||||
|
|
Loading…
Reference in a new issue