Basic implementation of if/else without conditional operators
This commit is contained in:
parent
0d93faf87f
commit
d4305b3629
|
@ -0,0 +1,57 @@
|
|||
# Grammar of the shulkerscript language
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Program
|
||||
```ebnf
|
||||
Program: Declaration*;
|
||||
```
|
||||
|
||||
### Declaration
|
||||
```ebnf
|
||||
Declaration: FunctionDeclaration;
|
||||
```
|
||||
|
||||
### FunctionDeclaration
|
||||
```ebnf
|
||||
Function:
|
||||
'fn' Identifier '(' ParameterList? ')' Block
|
||||
;
|
||||
ParameterList:
|
||||
Identifier (',' Identifier)* ','?
|
||||
;
|
||||
```
|
||||
|
||||
### Statement
|
||||
```ebnf
|
||||
Statement:
|
||||
Block
|
||||
| LiteralCommand
|
||||
| Conditional
|
||||
;
|
||||
```
|
||||
|
||||
### Block
|
||||
```ebnf
|
||||
Block: '{' Statement* '}';
|
||||
```
|
||||
|
||||
### Conditional
|
||||
```ebnf
|
||||
Conditional:
|
||||
'if' ParenthizedCondition Block ('else' Block)?
|
||||
;
|
||||
```
|
||||
|
||||
### ParenthizedCondition
|
||||
```ebnf
|
||||
ParenthizedCondition:
|
||||
'(' Condition ')'
|
||||
;
|
||||
```
|
||||
|
||||
### Condition
|
||||
```ebnf
|
||||
Condition:
|
||||
StringLiteral
|
||||
```
|
|
@ -3,3 +3,11 @@ pub trait Handler<T> {
|
|||
/// Receive an error and handles it.
|
||||
fn receive(&self, error: T);
|
||||
}
|
||||
|
||||
/// Is a struct that implements [`Handler`] trait by doing nothing with the errors.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Dummy;
|
||||
|
||||
impl<T> Handler<T> for Dummy {
|
||||
fn receive(&self, _error: T) {}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ mod error;
|
|||
pub use error::{Error, Result};
|
||||
|
||||
mod diagnostic;
|
||||
pub use diagnostic::Handler;
|
||||
pub use diagnostic::{Dummy, Handler};
|
||||
|
||||
pub mod log;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use shulkerbox::datapack::{Command, Datapack};
|
||||
use shulkerbox::datapack::{Command, Datapack, Execute};
|
||||
|
||||
use crate::{
|
||||
base::{source_file::SourceElement, Handler},
|
||||
|
@ -63,14 +63,61 @@ impl Compiler {
|
|||
fn compile_function(statements: &[Statement]) -> Vec<Command> {
|
||||
let mut commands = Vec::new();
|
||||
for statement in statements {
|
||||
match statement {
|
||||
Statement::LiteralCommand(literal_command) => {
|
||||
commands.push(literal_command.clean_command().into());
|
||||
}
|
||||
Statement::Block(_) => {
|
||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||
}
|
||||
}
|
||||
commands.extend(compile_statement(statement));
|
||||
}
|
||||
commands
|
||||
}
|
||||
|
||||
fn compile_statement(statement: &Statement) -> Option<Command> {
|
||||
match statement {
|
||||
Statement::LiteralCommand(literal_command) => Some(literal_command.clean_command().into()),
|
||||
Statement::Block(_) => {
|
||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||
}
|
||||
Statement::Conditional(cond) => {
|
||||
let (_, cond, block, el) = cond.clone().dissolve();
|
||||
let (_, cond, _) = cond.dissolve();
|
||||
let statements = block.statements();
|
||||
|
||||
let el = el
|
||||
.and_then(|el| {
|
||||
let (_, block) = el.dissolve();
|
||||
let statements = block.statements();
|
||||
if statements.is_empty() {
|
||||
None
|
||||
} else if statements.len() == 1 {
|
||||
compile_statement(&statements[0]).map(|cmd| Execute::Run(Box::new(cmd)))
|
||||
} else {
|
||||
let commands = statements.iter().filter_map(compile_statement).collect();
|
||||
Some(Execute::Runs(commands))
|
||||
}
|
||||
})
|
||||
.map(Box::new);
|
||||
|
||||
if statements.is_empty() {
|
||||
if el.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(Command::Execute(Execute::If(
|
||||
cond.value().string_content().into(),
|
||||
Box::new(Execute::Runs(Vec::new())),
|
||||
el,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
let run = if statements.len() > 1 {
|
||||
let commands = statements.iter().filter_map(compile_statement).collect();
|
||||
Execute::Runs(commands)
|
||||
} else {
|
||||
Execute::Run(Box::new(compile_statement(&statements[0])?))
|
||||
};
|
||||
|
||||
Some(Command::Execute(Execute::If(
|
||||
cond.value().string_content().into(),
|
||||
Box::new(run),
|
||||
el,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ pub enum Token {
|
|||
Punctuation(Punctuation),
|
||||
Numeric(Numeric),
|
||||
Comment(Comment),
|
||||
LiteralCommand(LiteralCommand),
|
||||
CommandLiteral(CommandLiteral),
|
||||
StringLiteral(StringLiteral),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
|
@ -88,7 +89,8 @@ impl Token {
|
|||
Self::Punctuation(token) => &token.span,
|
||||
Self::Numeric(token) => &token.span,
|
||||
Self::Comment(token) => &token.span,
|
||||
Self::LiteralCommand(token) => &token.span,
|
||||
Self::CommandLiteral(token) => &token.span,
|
||||
Self::StringLiteral(token) => &token.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +104,8 @@ impl SourceElement for Token {
|
|||
Self::Punctuation(token) => token.span(),
|
||||
Self::Numeric(token) => token.span(),
|
||||
Self::Comment(token) => token.span(),
|
||||
Self::LiteralCommand(token) => token.span(),
|
||||
Self::CommandLiteral(token) => token.span(),
|
||||
Self::StringLiteral(token) => token.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +180,28 @@ impl SourceElement for Numeric {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a hardcoded string literal value in the source code.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct StringLiteral {
|
||||
/// Is the span that makes up the token.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl StringLiteral {
|
||||
/// Returns the string without the leading and trailing double quotes.
|
||||
#[must_use]
|
||||
pub fn string_content(&self) -> &str {
|
||||
let string = self.span.str();
|
||||
&string[1..string.len() - 1]
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceElement for StringLiteral {
|
||||
fn span(&self) -> Span {
|
||||
self.span.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Is an enumeration representing the two kinds of comments in the Flux programming language.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum CommentKind {
|
||||
|
@ -205,17 +230,17 @@ impl SourceElement for Comment {
|
|||
|
||||
/// Represents a hardcoded literal command in the source code.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct LiteralCommand {
|
||||
pub struct CommandLiteral {
|
||||
/// Span that makes up the token.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl SourceElement for LiteralCommand {
|
||||
impl SourceElement for CommandLiteral {
|
||||
fn span(&self) -> Span {
|
||||
self.span.clone()
|
||||
}
|
||||
}
|
||||
impl LiteralCommand {
|
||||
impl CommandLiteral {
|
||||
/// Returns the command without the leading slash.
|
||||
#[must_use]
|
||||
pub fn clean_command(&self) -> &str {
|
||||
|
@ -367,7 +392,7 @@ impl Token {
|
|||
}
|
||||
// When there is no second slash and at the start of a line
|
||||
else if prev_token.map_or(true, |token| token.span().str().contains('\n')) {
|
||||
Ok(Self::handle_literal_command(iter, start))
|
||||
Ok(Self::handle_command_literal(iter, start))
|
||||
}
|
||||
// Just a single slash punctuation
|
||||
else {
|
||||
|
@ -390,11 +415,31 @@ impl Token {
|
|||
.into()
|
||||
}
|
||||
|
||||
/// Handles a sequence of characters that are enclosed in double quotes
|
||||
fn handle_string_literal(iter: &mut SourceIterator, start: usize) -> Self {
|
||||
let mut is_escaped = false;
|
||||
|
||||
for (_, character) in iter.by_ref() {
|
||||
if character == '\\' {
|
||||
is_escaped = !is_escaped;
|
||||
} else if character == '"' && !is_escaped {
|
||||
break;
|
||||
} else {
|
||||
is_escaped = false;
|
||||
}
|
||||
}
|
||||
|
||||
StringLiteral {
|
||||
span: Self::create_span(start, iter),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Handles a command that is preceeded by a slash
|
||||
fn handle_literal_command(iter: &mut SourceIterator, start: usize) -> Self {
|
||||
fn handle_command_literal(iter: &mut SourceIterator, start: usize) -> Self {
|
||||
Self::walk_iter(iter, |c| !(c.is_whitespace() && c.is_ascii_control()));
|
||||
|
||||
LiteralCommand {
|
||||
CommandLiteral {
|
||||
span: Self::create_span(start, iter),
|
||||
}
|
||||
.into()
|
||||
|
@ -431,6 +476,8 @@ impl Token {
|
|||
// Found comment/single slash punctuation
|
||||
else if character == '/' {
|
||||
Self::handle_comment(iter, start, character, prev_token, handler)
|
||||
} else if character == '"' {
|
||||
Ok(Self::handle_string_literal(iter, start))
|
||||
}
|
||||
// Found numeric literal
|
||||
else if character.is_ascii_digit() {
|
||||
|
|
|
@ -36,6 +36,8 @@ pub fn compile(path: PathBuf) -> Result<VFolder> {
|
|||
|
||||
let tokens = TokenStream::tokenize(&source_file, &printer);
|
||||
|
||||
// println!("tokens: {tokens:#?}");
|
||||
|
||||
if printer.has_printed() {
|
||||
return Err(Error::Other(
|
||||
"An error occurred while tokenizing the source code.",
|
||||
|
@ -47,7 +49,7 @@ pub fn compile(path: PathBuf) -> Result<VFolder> {
|
|||
"An error occured while parsing the source code.",
|
||||
))?;
|
||||
|
||||
// println!("result: {result:#?}");
|
||||
// println!("program: {program:#?}");
|
||||
|
||||
let mut compiler = Compiler::new();
|
||||
let datapack = compiler.compile(&program, &printer)?;
|
||||
|
|
|
@ -16,6 +16,7 @@ pub enum SyntaxKind {
|
|||
Identifier,
|
||||
Declaration,
|
||||
Numeric,
|
||||
StringLiteral,
|
||||
Statement,
|
||||
Expression,
|
||||
Type,
|
||||
|
@ -39,6 +40,7 @@ impl Display for UnexpectedSyntax {
|
|||
SyntaxKind::Keyword(keyword) => format!("a keyword token `{}`", keyword.as_str()),
|
||||
SyntaxKind::Declaration => "a declaration token".to_string(),
|
||||
SyntaxKind::Numeric => "a numeric token".to_string(),
|
||||
SyntaxKind::StringLiteral => "a string literal".to_string(),
|
||||
SyntaxKind::Statement => "a statement syntax".to_string(),
|
||||
SyntaxKind::Expression => "an expression syntax".to_string(),
|
||||
SyntaxKind::Type => "a type syntax".to_string(),
|
||||
|
@ -54,7 +56,8 @@ impl Display for UnexpectedSyntax {
|
|||
format!("a punctuation token `{}`", punctuation.punctuation)
|
||||
}
|
||||
Some(Token::Numeric(..)) => "a numeric token".to_string(),
|
||||
Some(Token::LiteralCommand(..)) => "a literal command token".to_string(),
|
||||
Some(Token::CommandLiteral(..)) => "a literal command token".to_string(),
|
||||
Some(Token::StringLiteral(..)) => "a string literal token".to_string(),
|
||||
|
||||
None => "EOF".to_string(),
|
||||
};
|
||||
|
|
|
@ -38,7 +38,7 @@ impl SourceElement for Declaration {
|
|||
///
|
||||
/// ``` ebnf
|
||||
/// Function:
|
||||
/// 'function' Identifier '(' ParameterList? ')' Block
|
||||
/// 'fn' Identifier '(' ParameterList? ')' Block
|
||||
/// ;
|
||||
///
|
||||
/// ParameterList:
|
||||
|
|
|
@ -1 +1,110 @@
|
|||
//! Syntax tree nodes for expressions.
|
||||
|
||||
use getset::Getters;
|
||||
|
||||
use crate::{
|
||||
base::{
|
||||
source_file::{SourceElement, Span},
|
||||
Handler,
|
||||
},
|
||||
lexical::{
|
||||
token::{Punctuation, StringLiteral, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
error::{Error, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// ParenthesizedCondition:
|
||||
/// '(' Condition ')';
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct ParenthesizedCondition {
|
||||
/// The opening parenthesis.
|
||||
#[get = "pub"]
|
||||
pub open_paren: Punctuation,
|
||||
/// The condition within the parenthesis.
|
||||
#[get = "pub"]
|
||||
pub condition: Box<Condition>,
|
||||
/// The closing parenthesis.
|
||||
#[get = "pub"]
|
||||
pub close_paren: Punctuation,
|
||||
}
|
||||
|
||||
impl ParenthesizedCondition {
|
||||
/// Dissolves the parenthesized condition into its components
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Punctuation, Condition, Punctuation) {
|
||||
(self.open_paren, *self.condition, self.close_paren)
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceElement for ParenthesizedCondition {
|
||||
fn span(&self) -> Span {
|
||||
self.open_paren
|
||||
.span()
|
||||
.join(&self.close_paren.span())
|
||||
.expect("The span of the parenthesis is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Condition: StringLiteral;
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct Condition {
|
||||
/// The value of the condition.
|
||||
#[get = "pub"]
|
||||
pub value: StringLiteral,
|
||||
}
|
||||
|
||||
impl SourceElement for Condition {
|
||||
fn span(&self) -> Span {
|
||||
self.value.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
/// Parses a [`Condition`].
|
||||
pub fn parse_condition(&mut self, handler: &impl Handler<Error>) -> Option<Condition> {
|
||||
match self.next_significant_token() {
|
||||
Reading::Atomic(Token::StringLiteral(s)) => Some(Condition { value: s }),
|
||||
unexpected => {
|
||||
// make progress
|
||||
self.forward();
|
||||
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: crate::syntax::error::SyntaxKind::Expression,
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a [`ParenthesizedCondition`].
|
||||
pub fn parse_parenthesized_condition(
|
||||
&mut self,
|
||||
handler: &impl Handler<Error>,
|
||||
) -> Option<ParenthesizedCondition> {
|
||||
let token_tree = self.step_into(
|
||||
Delimiter::Parenthesis,
|
||||
|parser| parser.parse_condition(handler),
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Some(ParenthesizedCondition {
|
||||
open_paren: token_tree.open,
|
||||
condition: Box::new(token_tree.tree?),
|
||||
close_paren: token_tree.close,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
Handler,
|
||||
},
|
||||
lexical::{
|
||||
token::{LiteralCommand, Punctuation, Token},
|
||||
token::{CommandLiteral, Keyword, KeywordKind, Punctuation, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
|
@ -17,20 +17,22 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::expression::ParenthesizedCondition;
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Statement:
|
||||
/// Block
|
||||
/// | Conditional
|
||||
/// | LiteralCommand
|
||||
/// ;
|
||||
/// ```
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Statement {
|
||||
Block(Block),
|
||||
LiteralCommand(LiteralCommand),
|
||||
// Conditional(Conditional),
|
||||
LiteralCommand(CommandLiteral),
|
||||
Conditional(Conditional),
|
||||
}
|
||||
|
||||
impl SourceElement for Statement {
|
||||
|
@ -38,7 +40,7 @@ impl SourceElement for Statement {
|
|||
match self {
|
||||
Self::Block(block) => block.span(),
|
||||
Self::LiteralCommand(literal_command) => literal_command.span(),
|
||||
//Self::Conditional(conditional) => conditional.span(),
|
||||
Self::Conditional(conditional) => conditional.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +82,87 @@ impl SourceElement for Block {
|
|||
}
|
||||
}
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Conditional:
|
||||
/// 'if' ParenthizedCondition Block ('else' Block)?
|
||||
/// ;
|
||||
/// ````
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct Conditional {
|
||||
/// The `if` keyword.
|
||||
#[get = "pub"]
|
||||
if_keyword: Keyword,
|
||||
/// The condition of the conditional.
|
||||
#[get = "pub"]
|
||||
condition: ParenthesizedCondition,
|
||||
/// The block of the conditional.
|
||||
#[get = "pub"]
|
||||
block: Block,
|
||||
/// The `else` statement.
|
||||
#[get = "pub"]
|
||||
r#else: Option<Else>,
|
||||
}
|
||||
|
||||
impl Conditional {
|
||||
/// Dissolves the [`Conditional`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, ParenthesizedCondition, Block, Option<Else>) {
|
||||
(self.if_keyword, self.condition, self.block, self.r#else)
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceElement for Conditional {
|
||||
fn span(&self) -> Span {
|
||||
self.r#else.as_ref().map_or_else(
|
||||
|| {
|
||||
self.if_keyword
|
||||
.span()
|
||||
.join(&self.block.span())
|
||||
.expect("The span of the conditional is invalid.")
|
||||
},
|
||||
|r#else| {
|
||||
self.if_keyword
|
||||
.span()
|
||||
.join(&r#else.span())
|
||||
.expect("The span of the else conditional is invalid.")
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Else:
|
||||
/// 'else' Block
|
||||
/// ;
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct Else {
|
||||
/// The `else` keyword.
|
||||
#[get = "pub"]
|
||||
else_keyword: Keyword,
|
||||
/// The block of the else statement.
|
||||
#[get = "pub"]
|
||||
block: Box<Block>,
|
||||
}
|
||||
|
||||
impl Else {
|
||||
/// Dissolves the [`Else`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, Box<Block>) {
|
||||
(self.else_keyword, self.block)
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceElement for Else {
|
||||
fn span(&self) -> Span {
|
||||
self.else_keyword.span().join(&self.block.span()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
/// Parses a [`Block`].
|
||||
pub fn parse_block(&mut self, handler: &impl Handler<Error>) -> Option<Block> {
|
||||
|
@ -123,7 +206,7 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_statement(&mut self, handler: &impl Handler<Error>) -> Option<Statement> {
|
||||
match self.stop_at_significant() {
|
||||
// variable declaration
|
||||
Reading::Atomic(Token::LiteralCommand(command)) => {
|
||||
Reading::Atomic(Token::CommandLiteral(command)) => {
|
||||
self.forward();
|
||||
Some(Statement::LiteralCommand(command))
|
||||
}
|
||||
|
@ -134,6 +217,46 @@ impl<'a> Parser<'a> {
|
|||
Some(Statement::Block(block))
|
||||
}
|
||||
|
||||
// conditional statement
|
||||
Reading::Atomic(Token::Keyword(if_keyword))
|
||||
if if_keyword.keyword == KeywordKind::If =>
|
||||
{
|
||||
// eat the if keyword
|
||||
self.forward();
|
||||
|
||||
let condition = self.parse_parenthesized_condition(handler)?;
|
||||
|
||||
let block = self.parse_block(handler)?;
|
||||
|
||||
match self.stop_at_significant() {
|
||||
// else statement
|
||||
Reading::Atomic(Token::Keyword(else_keyword))
|
||||
if else_keyword.keyword == KeywordKind::Else =>
|
||||
{
|
||||
// eat the else keyword
|
||||
self.forward();
|
||||
|
||||
let else_block = self.parse_block(handler)?;
|
||||
|
||||
Some(Statement::Conditional(Conditional {
|
||||
if_keyword,
|
||||
condition,
|
||||
block,
|
||||
r#else: Some(Else {
|
||||
else_keyword,
|
||||
block: Box::new(else_block),
|
||||
}),
|
||||
}))
|
||||
}
|
||||
// no else statement
|
||||
_ => Some(Statement::Conditional(Conditional {
|
||||
if_keyword,
|
||||
condition,
|
||||
block,
|
||||
r#else: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
// other
|
||||
unexpected => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
|
|
Loading…
Reference in New Issue