use std::str::FromStr;
use crate::brain_stem::ast::{Expression, Statement};
grammar;
match {
r"[0-9]+" => NUMBER,
r"[a-zA-Z_]\w*" => ID,
r#""[^"]*""# => STRING,
"'[^']'" => CHAR,
_,
// Skip whitespace and comments
r"\s*" => { },
r"//[^\n\r]*[\n\r]*" => { }, // `// comment`
r"/\*([^\*]*\*+[^\*/])*([^\*]*\*+|[^\*])*\*/" => { }, // `/* comment */`
}
pub Program : Vec<Statement<'input>> = {
<Statement*>,
}
Statement: Statement<'input> = {
"{" <s:Statement+> "}" => Statement::Block(s),
VarOrArrayDeclaration,
"if" <c:Expression> "then" "{" <t:Statement*> "}" <e:("else" "{" <Statement*> "}")?> =>
Statement::If(c, Box::new(Statement::Block(t)), e.map(Statement::Block).map(Box::new)),
"putc" "(" <Expression> ")" ";" => Statement::PutChar(<>),
"while" <c:Expression> "{" <b:Statement*> "}" => Statement::While(c, Box::new(Statement::Block(b))),
<Expression> ";" => Statement::Expression(<>),
"PushStackFrame" "(" ")" ";" => Statement::PushStackFrame(Vec::new()),
"PushStackFrame" "(" <mut h:(<Identifier> "=" <Expression> ",")*> <tn:Identifier> "=" <tv:Expression> ")" ";" => {
h.push((tn, tv));
Statement::PushStackFrame(h)
},
"PopStackFrame" "(" ")" ";" => Statement::PopStackFrame,
"MoveToStackFrameBelow" "(" <Identifier> ")" ";" => Statement::MoveToStackFrameBelow(<>),
};
VarOrArrayDeclaration: Statement<'input> = {
"var" <n:Identifier> "=" <i: Expression> ";" => Statement::VarDeclaration(n, i),
"var" <n:Identifier> "[" "]" "=" "[" <mut i: (<Expression> ",")*> <t:Expression> "]" ";" => {
i.push(t);
Statement::ArrayDeclaration(n, i)
},
"var" <n:Identifier> "[" "]" "=" <i:StringLiteral> ";" =>
Statement::ArrayDeclaration(n, i.chars().map(|c| Expression::Literal(c as i32)).collect()),
};
StringLiteral: &'input str = {
<l:STRING> => l[1..l.len() -1].into()
}
Expression: Expression<'input> = {
#[precedence(level="0")] // Highest precedence
Primary,
#[precedence(level="1")] #[assoc(side="left")]
"!" <e:Expression> => Expression::Not(Box::new(e)),
<o:"-"> <e:Expression> => Expression::Binary(Box::new(Expression::Literal(0)), o.into(), Box::new(e)),
<o:"+"> <e:Expression> => e,
#[precedence(level="2")] #[assoc(side="left")]
<l:Expression> <o:"*"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
<l:Expression> <o:"/"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
<l:Expression> <o:"%"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
#[precedence(level="3")] #[assoc(side="left")]
<l:Expression> <o:"+"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
<l:Expression> <o:"-"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
#[precedence(level="4")] #[assoc(side="left")]
<l:Expression> "<" <r:Expression> => Expression::Binary(Box::new(l), "<".into(), Box::new(r)),
<l:Expression> "<=" <r:Expression> => Expression::Binary(Box::new(l), "<=".into(), Box::new(r)),
// Swap r and l to implement gt and ge as lt and le.
<l:Expression> ">" <r:Expression> => Expression::Binary(Box::new(r), "<".into(), Box::new(l)),
<l:Expression> ">=" <r:Expression> => Expression::Binary(Box::new(r), "<=".into(), Box::new(l)),
#[precedence(level="5")] #[assoc(side="left")]
<l:Expression> <o:"!="> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
<l:Expression> <o:"=="> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
#[precedence(level="6")] #[assoc(side="left")]
<l:Expression> <o:"&&"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
#[precedence(level="7")] #[assoc(side="left")]
<l:Expression> <o:"||"> <r:Expression> => Expression::Binary(Box::new(l), o.into(), Box::new(r)),
#[precedence(level="8")] #[assoc(side="left")]
<n:Identifier> <i:("[" <Expression> "]")?> "=" <v:Expression> => match i {
Some(index) => Expression::ArrayAssignment(n, Box::new(index), Box::new(v)),
None => Expression::Assignment(n, Box::new(v)),
},
"LINMEM" "[" <a:Expression> "]" "=" <v:Expression> => Expression::MemoryWrite(Box::new(a), Box::new(v)),
}
Primary: Expression<'input> = {
NUMBER => Expression::Literal(i32::from_str(<>).unwrap()),
STRING => {
assert_eq!(<>.len(), 3);
Expression::Literal(<>.chars().nth(1).unwrap() as i32)
},
<n:Identifier> <i:("[" <Expression> "]")?> => match i {
None => Expression::Variable(n),
Some(index) => Expression::ArrayLookup(n, Box::new(index))
},
"LINMEM" "[" <Expression> "]" => Expression::MemoryRead(Box::new(<>)),
"(" <Expression> ")",
};
Identifier = ID;