First part of reporting locations in error messages
This commit is contained in:
+20
-2
@@ -1,5 +1,23 @@
|
|||||||
|
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||||
|
pub struct Location {
|
||||||
|
pub line: usize,
|
||||||
|
pub col: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(usize, usize)> for Location {
|
||||||
|
fn from(value: (usize, usize)) -> Self {
|
||||||
|
Self { line: value.0, col: value.1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub struct Program(pub Vec<Declaration>);
|
pub struct Located<T> {
|
||||||
|
pub location: Location,
|
||||||
|
pub ast: T
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
pub struct Program(pub Vec<Located<Declaration>>);
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
@@ -22,7 +40,7 @@ pub struct Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub struct Block(pub Vec<Statement>);
|
pub struct Block(pub Vec<Located<Statement>>);
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
|
|||||||
+39
-35
@@ -157,20 +157,23 @@ impl State {
|
|||||||
trait Compilable {
|
trait Compilable {
|
||||||
// Every AST node we visit can see the global compiler state (to make unique
|
// Every AST node we visit can see the global compiler state (to make unique
|
||||||
// symbols and reach for global names) as well as optionally the current
|
// symbols and reach for global names) as well as optionally the current
|
||||||
// function (for AST nodes within functions)
|
// function (for AST nodes within functions). Additionally se send a Location:
|
||||||
|
// This is the closest ancestor's location, in case we need to emit a compile
|
||||||
|
// error
|
||||||
fn process(
|
fn process(
|
||||||
self,
|
self,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
function: Option<&mut CompiledFn>,
|
function: Option<&mut CompiledFn>,
|
||||||
|
location: Location
|
||||||
) -> Result<(), CompileError>;
|
) -> Result<(), CompileError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Program {
|
impl Compilable for Program {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
for decl in self.0 {
|
for decl in self.0 {
|
||||||
decl.process(state, None)?
|
decl.ast.process(state, None, decl.location)?
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -179,11 +182,11 @@ impl Compilable for Program {
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Declaration {
|
impl Compilable for Declaration {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
match self {
|
match self {
|
||||||
Declaration::Function(f) => f.process(state, None),
|
Declaration::Function(f) => f.process(state, None, loc),
|
||||||
Declaration::Global(g) => g.process(state, None),
|
Declaration::Global(g) => g.process(state, None, loc),
|
||||||
Declaration::Const(c) => c.process(state, None),
|
Declaration::Const(c) => c.process(state, None, loc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +194,7 @@ impl Compilable for Declaration {
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Function {
|
impl Compilable for Function {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
// The signature for this function, which will eventually get added to the state
|
// The signature for this function, which will eventually get added to the state
|
||||||
let mut sig = CompiledFn {
|
let mut sig = CompiledFn {
|
||||||
label: state.gensym(),
|
label: state.gensym(),
|
||||||
@@ -213,21 +216,22 @@ impl Compilable for Function {
|
|||||||
|
|
||||||
// Compile each statement:
|
// Compile each statement:
|
||||||
for stmt in self.body.0 {
|
for stmt in self.body.0 {
|
||||||
match stmt {
|
let loc = stmt.location;
|
||||||
|
match stmt.ast {
|
||||||
Statement::Return(_) => {}
|
Statement::Return(_) => {}
|
||||||
Statement::Assignment(assign) => assign.process(state, Some(&mut sig))?,
|
Statement::Assignment(assign) => assign.process(state, Some(&mut sig), loc)?,
|
||||||
Statement::Expr(expr) => {
|
Statement::Expr(expr) => {
|
||||||
expr.process(state, Some(&mut sig))?;
|
expr.process(state, Some(&mut sig), loc)?;
|
||||||
// Every expr leaves a single-word return value on the stack. In an rvalue this
|
// Every expr leaves a single-word return value on the stack. In an rvalue this
|
||||||
// is useful but in a statement it's garbage (because nothing else is about to
|
// is useful but in a statement it's garbage (because nothing else is about to
|
||||||
// pick it up) so, drop it:
|
// pick it up) so, drop it:
|
||||||
sig.emit("pop")
|
sig.emit("pop")
|
||||||
}
|
}
|
||||||
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig))?,
|
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig), loc)?,
|
||||||
Statement::Asm(Asm { args, body}) => {
|
Statement::Asm(Asm { args, body}) => {
|
||||||
// Process all the args, if any
|
// Process all the args, if any
|
||||||
for a in args {
|
for a in args {
|
||||||
(*a.0).process(state, Some(&mut sig))?
|
(*a.0).process(state, Some(&mut sig), loc)?
|
||||||
}
|
}
|
||||||
// Emit the body
|
// Emit the body
|
||||||
sig.emit(body.as_str())
|
sig.emit(body.as_str())
|
||||||
@@ -259,12 +263,12 @@ fn lookup<'a>(name: &str, global_scope: &'a Scope, local_scope: &'a Scope) -> Op
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Assignment {
|
impl Compilable for Assignment {
|
||||||
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let Assignment { lvalue, rvalue } = self;
|
let Assignment { lvalue, rvalue } = self;
|
||||||
let sig = sig.expect("Assignment outside function");
|
let sig = sig.expect("Assignment outside function");
|
||||||
// First the value, then the address we'll storew it to
|
// First the value, then the address we'll storew it to
|
||||||
rvalue.process(state, Some(sig))?;
|
rvalue.process(state, Some(sig), loc)?;
|
||||||
lvalue.process(state, Some(sig))?;
|
lvalue.process(state, Some(sig), loc)?;
|
||||||
sig.emit("storew");
|
sig.emit("storew");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -273,7 +277,7 @@ impl Compilable for Assignment {
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for VarDecl {
|
impl Compilable for VarDecl {
|
||||||
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let sig = sig.expect("Var declaration outside function");
|
let sig = sig.expect("Var declaration outside function");
|
||||||
if self.size.is_some() {
|
if self.size.is_some() {
|
||||||
todo!("Arrays are not yet supported")
|
todo!("Arrays are not yet supported")
|
||||||
@@ -281,11 +285,11 @@ impl Compilable for VarDecl {
|
|||||||
if let Some(initial) = self.initial {
|
if let Some(initial) = self.initial {
|
||||||
// If it's got an initial value, we have to compile that before we add
|
// If it's got an initial value, we have to compile that before we add
|
||||||
// the name to scope, or else UB will ensue if it refers to itself:
|
// the name to scope, or else UB will ensue if it refers to itself:
|
||||||
initial.process(state, Some(sig))?;
|
initial.process(state, Some(sig), loc)?;
|
||||||
// But then add it to scope and assign:
|
// But then add it to scope and assign:
|
||||||
sig.add_local(&self.name)?;
|
sig.add_local(&self.name)?;
|
||||||
// We'll just whip up an lvalue real quick...
|
// We'll just whip up an lvalue real quick...
|
||||||
Lvalue::from(self.name).process(state, Some(sig))?;
|
Lvalue::from(self.name).process(state, Some(sig), loc)?;
|
||||||
sig.emit("storew"); // And store the initial value there
|
sig.emit("storew"); // And store the initial value there
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just add it to scope and leave garbage in there:
|
// Otherwise, just add it to scope and leave garbage in there:
|
||||||
@@ -299,7 +303,7 @@ impl Compilable for VarDecl {
|
|||||||
|
|
||||||
/// Evaluate an lvalue and leave its address on the stack (ready to be consumed by storew)
|
/// Evaluate an lvalue and leave its address on the stack (ready to be consumed by storew)
|
||||||
impl Compilable for Lvalue {
|
impl Compilable for Lvalue {
|
||||||
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let global_scope = &state.global_scope;
|
let global_scope = &state.global_scope;
|
||||||
let sig = sig.expect("lvalue outside a function");
|
let sig = sig.expect("lvalue outside a function");
|
||||||
|
|
||||||
@@ -346,7 +350,7 @@ impl Compilable for Lvalue {
|
|||||||
}
|
}
|
||||||
// Derefs are just evaluating the expr and leaving its value (an address) on the stack
|
// Derefs are just evaluating the expr and leaving its value (an address) on the stack
|
||||||
Expr::Deref(BoxExpr(expr)) => {
|
Expr::Deref(BoxExpr(expr)) => {
|
||||||
(*expr).process(state, Some(sig))
|
(*expr).process(state, Some(sig), loc)
|
||||||
}
|
}
|
||||||
Expr::Subscript(_, _) => {
|
Expr::Subscript(_, _) => {
|
||||||
Err(CompileError(0, 0, String::from("Arrays are not yet supported")))
|
Err(CompileError(0, 0, String::from("Arrays are not yet supported")))
|
||||||
@@ -360,7 +364,7 @@ impl Compilable for Lvalue {
|
|||||||
/// Evaluate an expression in the context of a local scope. The runtime brother to eval_const.
|
/// Evaluate an expression in the context of a local scope. The runtime brother to eval_const.
|
||||||
/// This recursively evaluates a Node and leaves its value on the stack.
|
/// This recursively evaluates a Node and leaves its value on the stack.
|
||||||
impl Compilable for Expr {
|
impl Compilable for Expr {
|
||||||
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let mut sig = sig.expect("Non-const expression outside a function");
|
let mut sig = sig.expect("Non-const expression outside a function");
|
||||||
let global_scope = &state.global_scope;
|
let global_scope = &state.global_scope;
|
||||||
|
|
||||||
@@ -412,21 +416,21 @@ impl Compilable for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Neg(e) => {
|
Expr::Neg(e) => {
|
||||||
(*e.0).process(state, Some(sig))?;
|
(*e.0).process(state, Some(sig), loc)?;
|
||||||
// To arithmetically negate something, invert and increment (2s complement)
|
// To arithmetically negate something, invert and increment (2s complement)
|
||||||
sig.emit("xor -1");
|
sig.emit("xor -1");
|
||||||
sig.emit("add 1");
|
sig.emit("add 1");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Not(e) => {
|
Expr::Not(e) => {
|
||||||
(*e.0).process(state, Some(sig))?;
|
(*e.0).process(state, Some(sig), loc)?;
|
||||||
sig.emit("not");
|
sig.emit("not");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Handling addresses is very easy because processing an lvalue leaves the address on the stack
|
// Handling addresses is very easy because processing an lvalue leaves the address on the stack
|
||||||
Expr::Address(lvalue) => lvalue.process(state, Some(sig)),
|
Expr::Address(lvalue) => lvalue.process(state, Some(sig), loc),
|
||||||
Expr::Deref(BoxExpr(e)) => {
|
Expr::Deref(BoxExpr(e)) => {
|
||||||
(*e).process(state, Some(sig))?;
|
(*e).process(state, Some(sig), loc)?;
|
||||||
sig.emit("loadw");
|
sig.emit("loadw");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -439,8 +443,8 @@ impl Compilable for Expr {
|
|||||||
Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"),
|
Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"),
|
||||||
Expr::Infix(lhs, op, rhs) => {
|
Expr::Infix(lhs, op, rhs) => {
|
||||||
// Recurse on expressions, handling operators
|
// Recurse on expressions, handling operators
|
||||||
(*lhs.0).process(state, Some(&mut sig))?;
|
(*lhs.0).process(state, Some(&mut sig), loc)?;
|
||||||
(*rhs.0).process(state, Some(&mut sig))?;
|
(*rhs.0).process(state, Some(&mut sig), loc)?;
|
||||||
match op {
|
match op {
|
||||||
// Basic math
|
// Basic math
|
||||||
Operator::Add => sig.emit("add"),
|
Operator::Add => sig.emit("add"),
|
||||||
@@ -493,7 +497,7 @@ impl Compilable for Expr {
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Global {
|
impl Compilable for Global {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
if self.size.is_some() {
|
if self.size.is_some() {
|
||||||
todo!("Arrays are not yet supported")
|
todo!("Arrays are not yet supported")
|
||||||
}
|
}
|
||||||
@@ -504,7 +508,7 @@ impl Compilable for Global {
|
|||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl Compilable for Const {
|
impl Compilable for Const {
|
||||||
fn process(self, state: &mut State, _: Option<&mut CompiledFn>) -> Result<(), CompileError> {
|
fn process(self, state: &mut State, _: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
|
||||||
let var = if self.string.is_some() {
|
let var = if self.string.is_some() {
|
||||||
// If it's a string, add it to the string table
|
// If it's a string, add it to the string table
|
||||||
Variable::DirectLabel(state.add_string(&self.string.unwrap()))
|
Variable::DirectLabel(state.add_string(&self.string.unwrap()))
|
||||||
@@ -625,7 +629,7 @@ mod test {
|
|||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
parse("const foo = 17 + 3;")
|
parse("const foo = 17 + 3;")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process(&mut state, None)
|
.process(&mut state, None, (0, 0).into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.global_scope,
|
state.global_scope,
|
||||||
@@ -638,7 +642,7 @@ mod test {
|
|||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
parse("const foo = \"bar\";")
|
parse("const foo = \"bar\";")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process(&mut state, None)
|
.process(&mut state, None, (0, 0).into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.global_scope,
|
state.global_scope,
|
||||||
@@ -651,7 +655,7 @@ mod test {
|
|||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
parse("global a;")
|
parse("global a;")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process(&mut state, None)
|
.process(&mut state, None, (0, 0).into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.global_scope,
|
state.global_scope,
|
||||||
@@ -664,7 +668,7 @@ mod test {
|
|||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
assert!(parse("const a = 7; global a;")
|
assert!(parse("const a = 7; global a;")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process(&mut state, None)
|
.process(&mut state, None, (0, 0).into())
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,7 +676,7 @@ mod test {
|
|||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
parse(src)
|
parse(src)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process(&mut state, None)
|
.process(&mut state, None, (0, 0).into())
|
||||||
.expect("Failed to compile");
|
.expect("Failed to compile");
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,13 @@ impl From<pest::error::Error<Rule>> for ParseError {
|
|||||||
trait AstNode: Sized {
|
trait AstNode: Sized {
|
||||||
const RULE: Rule;
|
const RULE: Rule;
|
||||||
fn from_pair(pair: Pair) -> Self;
|
fn from_pair(pair: Pair) -> Self;
|
||||||
|
fn from_pair_located(pair: Pair) -> Located<Self> {
|
||||||
|
let loc = pair.line_col();
|
||||||
|
Located {
|
||||||
|
location: loc.into(),
|
||||||
|
ast: Self::from_pair(pair)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Parseable: Sized {
|
trait Parseable: Sized {
|
||||||
@@ -307,7 +314,7 @@ impl AstNode for Block {
|
|||||||
const RULE: Rule = Rule::block;
|
const RULE: Rule = Rule::block;
|
||||||
|
|
||||||
fn from_pair(pair: Pair) -> Self {
|
fn from_pair(pair: Pair) -> Self {
|
||||||
Self(pair.into_inner().map(Statement::from_pair).collect())
|
Self(pair.into_inner().map(Statement::from_pair_located).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +454,7 @@ impl AstNode for Program {
|
|||||||
Self(
|
Self(
|
||||||
pair.into_inner()
|
pair.into_inner()
|
||||||
.filter(|p| p.as_rule() != Rule::EOI)
|
.filter(|p| p.as_rule() != Rule::EOI)
|
||||||
.map(Declaration::from_pair)
|
.map(Declaration::from_pair_located)
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -770,66 +777,79 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_block() {
|
fn parse_block() {
|
||||||
|
let block = Block::from_str("{ foo(); bar(); }").unwrap();
|
||||||
|
let statements: Vec<_> = block.0.into_iter().map(|s| s.ast).collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Block::from_str("{ foo(); bar(); }"),
|
statements,
|
||||||
Ok(Block(vec![
|
vec![
|
||||||
Statement::Expr(Expr::Call("foo".into(), vec![])),
|
Statement::Expr(Expr::Call("foo".into(), vec![])),
|
||||||
Statement::Expr(Expr::Call("bar".into(), vec![])),
|
Statement::Expr(Expr::Call("bar".into(), vec![])),
|
||||||
]))
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dislocate<T>(block: Vec<Located<T>>) -> Vec<T> {
|
||||||
|
block.into_iter().map(|l| l.ast).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dislocated_block(src: &str) -> Vec<Statement> {
|
||||||
|
dislocate(Block::from_str(src).unwrap().0)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_conditional() {
|
fn parse_conditional() {
|
||||||
assert_eq!(
|
if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
|
||||||
Statement::from_str("if(cond) { foo(); }"),
|
Statement::from_str("if(cond) { foo(); }") {
|
||||||
Ok(Statement::Conditional(Conditional {
|
assert_eq!(condition, Expr::from_str("cond").unwrap());
|
||||||
condition: Expr::from_str("cond").unwrap(),
|
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
|
||||||
body: Block::from_str("{ foo(); }").unwrap(),
|
assert_eq!(alternative, None);
|
||||||
alternative: None,
|
} else {
|
||||||
}))
|
panic!()
|
||||||
);
|
}
|
||||||
|
|
||||||
assert_eq!(
|
if let Ok(Statement::Conditional(Conditional { condition, body, alternative })) =
|
||||||
Statement::from_str("if(cond) { foo(); } else { bar(); }"),
|
Statement::from_str("if(cond) { foo(); } else { bar(); }") {
|
||||||
Ok(Statement::Conditional(Conditional {
|
assert_eq!(condition, Expr::from_str("cond").unwrap());
|
||||||
condition: Expr::from_str("cond").unwrap(),
|
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
|
||||||
body: Block::from_str("{ foo(); }").unwrap(),
|
assert_eq!(alternative.unwrap().0[0].ast, Block::from_str("{ bar(); }").unwrap().0[0].ast);
|
||||||
alternative: Some(Block::from_str("{ bar(); }").unwrap()),
|
} else {
|
||||||
}))
|
panic!()
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_while_loops() {
|
fn parse_while_loops() {
|
||||||
assert_eq!(
|
assert!(match Statement::from_str("while(cond) { foo(); }") {
|
||||||
Statement::from_str("while(cond) { foo(); }"),
|
Ok(Statement::WhileLoop(WhileLoop { condition, body })) => {
|
||||||
Ok(Statement::WhileLoop(WhileLoop {
|
assert_eq!(condition, Expr::from_str("cond").unwrap());
|
||||||
condition: Expr::from_str("cond").unwrap(),
|
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
|
||||||
body: Block::from_str("{ foo(); }").unwrap(),
|
true
|
||||||
}))
|
}
|
||||||
);
|
_ => false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_repeat_loops() {
|
fn parse_repeat_loops() {
|
||||||
assert_eq!(
|
assert!(match Statement::from_str("repeat(10) x { foo(x); }") {
|
||||||
Statement::from_str("repeat(10) x { foo(x); }"),
|
Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
|
||||||
Ok(Statement::RepeatLoop(RepeatLoop {
|
assert_eq!(count, 10.into());
|
||||||
count: 10.into(),
|
assert_eq!(name, Some("x".into()));
|
||||||
name: Some("x".into()),
|
assert_eq!(dislocate(body.0), dislocated_block("{ foo(x); }"));
|
||||||
body: Block::from_str("{ foo(x); }").unwrap(),
|
true
|
||||||
}))
|
},
|
||||||
);
|
_ => false
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert!(match Statement::from_str("repeat(10) { foo(); }") {
|
||||||
Statement::from_str("repeat(10) { foo(); }"),
|
Ok(Statement::RepeatLoop(RepeatLoop { count, name, body })) => {
|
||||||
Ok(Statement::RepeatLoop(RepeatLoop {
|
assert_eq!(count, 10.into());
|
||||||
count: 10.into(),
|
assert_eq!(name, None);
|
||||||
name: None,
|
assert_eq!(dislocate(body.0), dislocated_block("{ foo(); }"));
|
||||||
body: Block::from_str("{ foo(); }").unwrap(),
|
true
|
||||||
}))
|
},
|
||||||
);
|
_ => false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -847,12 +867,14 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_program() {
|
fn parse_program() {
|
||||||
|
let prog = Program::from_str("global foo; const blah = 3;").unwrap();
|
||||||
|
let decls: Vec<_> = dislocate(prog.0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Program::from_str("global foo; fn blah(x) { return x + 3; }"),
|
decls,
|
||||||
Ok(Program(vec![
|
vec![
|
||||||
Declaration::from_str("global foo;").unwrap(),
|
Declaration::from_str("global foo;").unwrap(),
|
||||||
Declaration::from_str("fn blah(x) { return x + 3; }").unwrap(),
|
Declaration::from_str("const blah = 3;").unwrap(),
|
||||||
]))
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user