Merge pull request #4 from dbidwell94/logic_expressions
Logic expressions
This commit is contained in:
158
libs/compiler/src/test/branching.rs
Normal file
158
libs/compiler/src/test/branching.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use crate::compile;
|
||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_statement() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 10;
|
||||||
|
if (a > 5) {
|
||||||
|
a = 20;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 10 #a
|
||||||
|
sgt r1 r8 5
|
||||||
|
beq r1 0 L1
|
||||||
|
move r8 20 #a
|
||||||
|
L1:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_else_statement() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
if (10 > 5) {
|
||||||
|
a = 1;
|
||||||
|
} else {
|
||||||
|
a = 2;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
sgt r1 10 5
|
||||||
|
beq r1 0 L2
|
||||||
|
move r8 1 #a
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
move r8 2 #a
|
||||||
|
L1:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_else_if_statement() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
if (a == 1) {
|
||||||
|
a = 10;
|
||||||
|
} else if (a == 2) {
|
||||||
|
a = 20;
|
||||||
|
} else {
|
||||||
|
a = 30;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
seq r1 r8 1
|
||||||
|
beq r1 0 L2
|
||||||
|
move r8 10 #a
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
seq r2 r8 2
|
||||||
|
beq r2 0 L4
|
||||||
|
move r8 20 #a
|
||||||
|
j L3
|
||||||
|
L4:
|
||||||
|
move r8 30 #a
|
||||||
|
L3:
|
||||||
|
L1:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spilled_variable_update_in_branch() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 1;
|
||||||
|
let b = 2;
|
||||||
|
let c = 3;
|
||||||
|
let d = 4;
|
||||||
|
let e = 5;
|
||||||
|
let f = 6;
|
||||||
|
let g = 7;
|
||||||
|
let h = 8; // Spilled to stack (offset 0)
|
||||||
|
|
||||||
|
if (a == 1) {
|
||||||
|
h = 99;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 1 #a
|
||||||
|
move r9 2 #b
|
||||||
|
move r10 3 #c
|
||||||
|
move r11 4 #d
|
||||||
|
move r12 5 #e
|
||||||
|
move r13 6 #f
|
||||||
|
move r14 7 #g
|
||||||
|
push 8 #h
|
||||||
|
seq r1 r8 1
|
||||||
|
beq r1 0 L1
|
||||||
|
sub r0 sp 1
|
||||||
|
put db r0 99 #h
|
||||||
|
L1:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
@@ -85,3 +85,63 @@ fn variable_declaration_negative() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boolean_declaration() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let t = true;
|
||||||
|
let f = false;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 1 #t
|
||||||
|
move r9 0 #f
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boolean_return() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
fn getTrue() {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
let val = getTrue();
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
getTrue:
|
||||||
|
push ra
|
||||||
|
move r15 1 #returnValue
|
||||||
|
sub r0 sp 1
|
||||||
|
get ra db r0
|
||||||
|
sub sp sp 1
|
||||||
|
j ra
|
||||||
|
main:
|
||||||
|
jal getTrue
|
||||||
|
move r8 r15 #val
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
177
libs/compiler/src/test/logic_expression.rs
Normal file
177
libs/compiler/src/test/logic_expression.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
use crate::compile;
|
||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comparison_expressions() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let isGreater = 10 > 5;
|
||||||
|
let isLess = 5 < 10;
|
||||||
|
let isEqual = 5 == 5;
|
||||||
|
let isNotEqual = 5 != 10;
|
||||||
|
let isGreaterOrEqual = 10 >= 10;
|
||||||
|
let isLessOrEqual = 5 <= 5;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
sgt r1 10 5
|
||||||
|
move r8 r1 #isGreater
|
||||||
|
slt r2 5 10
|
||||||
|
move r9 r2 #isLess
|
||||||
|
seq r3 5 5
|
||||||
|
move r10 r3 #isEqual
|
||||||
|
sne r4 5 10
|
||||||
|
move r11 r4 #isNotEqual
|
||||||
|
sge r5 10 10
|
||||||
|
move r12 r5 #isGreaterOrEqual
|
||||||
|
sle r6 5 5
|
||||||
|
move r13 r6 #isLessOrEqual
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_and_or_not() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let logic1 = 1 && 1;
|
||||||
|
let logic2 = 1 || 0;
|
||||||
|
let logic3 = !1;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
and r1 1 1
|
||||||
|
move r8 r1 #logic1
|
||||||
|
or r2 1 0
|
||||||
|
move r9 r2 #logic2
|
||||||
|
seq r3 1 0
|
||||||
|
move r10 r3 #logic3
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complex_logic() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let logic = (10 > 5) && (5 < 10);
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
sgt r1 10 5
|
||||||
|
slt r2 5 10
|
||||||
|
and r3 r1 r2
|
||||||
|
move r8 r3 #logic
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math_with_logic() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let logic = (1 + 2) > 1;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
add r1 1 2
|
||||||
|
sgt r2 r1 1
|
||||||
|
move r8 r2 #logic
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boolean_in_logic() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let res = true && false;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
and r1 1 0
|
||||||
|
move r8 r1 #res
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invert_a_boolean() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let i = true;
|
||||||
|
let y = !i;
|
||||||
|
|
||||||
|
let result = y == false;
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 1 #i
|
||||||
|
seq r1 r8 0
|
||||||
|
move r9 r1 #y
|
||||||
|
seq r2 r9 0
|
||||||
|
move r10 r2 #result
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
149
libs/compiler/src/test/loops.rs
Normal file
149
libs/compiler/src/test/loops.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
use crate::compile;
|
||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_infinite_loop() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
loop {
|
||||||
|
a = a + 1;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Labels: L1 (start), L2 (end)
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
L1:
|
||||||
|
add r1 r8 1
|
||||||
|
move r8 r1 #a
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_break() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
loop {
|
||||||
|
a = a + 1;
|
||||||
|
if (a > 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Labels: L1 (start), L2 (end), L3 (if end - implicit else label)
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
L1:
|
||||||
|
add r1 r8 1
|
||||||
|
move r8 r1 #a
|
||||||
|
sgt r2 r8 10
|
||||||
|
beq r2 0 L3
|
||||||
|
j L2
|
||||||
|
L3:
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_while_loop() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
while (a < 10) {
|
||||||
|
a = a + 1;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Labels: L1 (start), L2 (end)
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
L1:
|
||||||
|
slt r1 r8 10
|
||||||
|
beq r1 0 L2
|
||||||
|
add r2 r8 1
|
||||||
|
move r8 r2 #a
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_continue() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
let a = 0;
|
||||||
|
loop {
|
||||||
|
a = a + 1;
|
||||||
|
if (a < 5) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Labels: L1 (start), L2 (end), L3 (if end)
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
L1:
|
||||||
|
add r1 r8 1
|
||||||
|
move r8 r1 #a
|
||||||
|
slt r2 r8 5
|
||||||
|
beq r2 0 L3
|
||||||
|
j L1
|
||||||
|
L3:
|
||||||
|
j L2
|
||||||
|
j L1
|
||||||
|
L2:
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -41,6 +41,9 @@ macro_rules! compile {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
mod binary_expression;
|
mod binary_expression;
|
||||||
|
mod branching;
|
||||||
mod declaration_function_invocation;
|
mod declaration_function_invocation;
|
||||||
mod declaration_literal;
|
mod declaration_literal;
|
||||||
mod function_declaration;
|
mod function_declaration;
|
||||||
|
mod logic_expression;
|
||||||
|
mod loops;
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use crate::variable_manager::{self, LocationRequest, VariableLocation, VariableS
|
|||||||
use parser::{
|
use parser::{
|
||||||
Parser as ASTParser,
|
Parser as ASTParser,
|
||||||
tree_node::{
|
tree_node::{
|
||||||
BinaryExpression, BlockExpression, DeviceDeclarationExpression, Expression,
|
AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression,
|
||||||
FunctionExpression, InvocationExpression, Literal,
|
Expression, FunctionExpression, IfExpression, InvocationExpression, Literal,
|
||||||
|
LogicalExpression, LoopExpression, WhileExpression,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
@@ -75,6 +76,8 @@ pub struct Compiler<'a, W: std::io::Write> {
|
|||||||
declared_main: bool,
|
declared_main: bool,
|
||||||
config: CompilerConfig,
|
config: CompilerConfig,
|
||||||
temp_counter: usize,
|
temp_counter: usize,
|
||||||
|
label_counter: usize,
|
||||||
|
loop_stack: Vec<(String, String)>, // Stores (start_label, end_label)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: std::io::Write> Compiler<'a, W> {
|
impl<'a, W: std::io::Write> Compiler<'a, W> {
|
||||||
@@ -93,6 +96,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
declared_main: false,
|
declared_main: false,
|
||||||
config: config.unwrap_or_default(),
|
config: config.unwrap_or_default(),
|
||||||
temp_counter: 0,
|
temp_counter: 0,
|
||||||
|
label_counter: 0,
|
||||||
|
loop_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +125,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
format!("__binary_temp_{}", self.temp_counter)
|
format!("__binary_temp_{}", self.temp_counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_label_name(&mut self) -> String {
|
||||||
|
self.label_counter += 1;
|
||||||
|
format!("L{}", self.label_counter)
|
||||||
|
}
|
||||||
|
|
||||||
fn expression<'v>(
|
fn expression<'v>(
|
||||||
&mut self,
|
&mut self,
|
||||||
expr: Expression,
|
expr: Expression,
|
||||||
@@ -134,6 +144,26 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
self.expression_block(expr_block, scope)?;
|
self.expression_block(expr_block, scope)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
Expression::If(expr_if) => {
|
||||||
|
self.expression_if(expr_if, scope)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Expression::Loop(expr_loop) => {
|
||||||
|
self.expression_loop(expr_loop, scope)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Expression::While(expr_while) => {
|
||||||
|
self.expression_while(expr_while, scope)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Expression::Break => {
|
||||||
|
self.expression_break()?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Expression::Continue => {
|
||||||
|
self.expression_continue()?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
Expression::DeviceDeclaration(expr_dev) => {
|
Expression::DeviceDeclaration(expr_dev) => {
|
||||||
self.expression_device(expr_dev)?;
|
self.expression_device(expr_dev)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@@ -145,6 +175,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
temp_name: None,
|
temp_name: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
Expression::Assignment(assign_expr) => {
|
||||||
|
self.expression_assignment(assign_expr, scope)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
Expression::Invocation(expr_invoke) => {
|
Expression::Invocation(expr_invoke) => {
|
||||||
self.expression_function_invocation(expr_invoke, scope)?;
|
self.expression_function_invocation(expr_invoke, scope)?;
|
||||||
// Invocation returns result in r15 (RETURN_REGISTER).
|
// Invocation returns result in r15 (RETURN_REGISTER).
|
||||||
@@ -165,6 +199,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
let result = self.expression_binary(bin_expr, scope)?;
|
let result = self.expression_binary(bin_expr, scope)?;
|
||||||
Ok(Some(result))
|
Ok(Some(result))
|
||||||
}
|
}
|
||||||
|
Expression::Logical(log_expr) => {
|
||||||
|
let result = self.expression_logical(log_expr, scope)?;
|
||||||
|
Ok(Some(result))
|
||||||
|
}
|
||||||
Expression::Literal(Literal::Number(num)) => {
|
Expression::Literal(Literal::Number(num)) => {
|
||||||
let temp_name = self.next_temp_name();
|
let temp_name = self.next_temp_name();
|
||||||
let loc = scope.add_variable(&temp_name, LocationRequest::Temp)?;
|
let loc = scope.add_variable(&temp_name, LocationRequest::Temp)?;
|
||||||
@@ -174,6 +212,16 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
temp_name: Some(temp_name),
|
temp_name: Some(temp_name),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
Expression::Literal(Literal::Boolean(b)) => {
|
||||||
|
let val = if b { "1" } else { "0" };
|
||||||
|
let temp_name = self.next_temp_name();
|
||||||
|
let loc = scope.add_variable(&temp_name, LocationRequest::Temp)?;
|
||||||
|
self.emit_variable_assignment(&temp_name, &loc, val)?;
|
||||||
|
Ok(Some(CompilationResult {
|
||||||
|
location: loc,
|
||||||
|
temp_name: Some(temp_name),
|
||||||
|
}))
|
||||||
|
}
|
||||||
Expression::Variable(name) => {
|
Expression::Variable(name) => {
|
||||||
let loc = scope.get_location_of(&name)?;
|
let loc = scope.get_location_of(&name)?;
|
||||||
Ok(Some(CompilationResult {
|
Ok(Some(CompilationResult {
|
||||||
@@ -254,6 +302,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
self.emit_variable_assignment(&var_name, &var_location, num)?;
|
self.emit_variable_assignment(&var_name, &var_location, num)?;
|
||||||
var_location
|
var_location
|
||||||
}
|
}
|
||||||
|
Expression::Literal(Literal::Boolean(b)) => {
|
||||||
|
let val = if b { "1" } else { "0" };
|
||||||
|
let var_location =
|
||||||
|
scope.add_variable(var_name.clone(), LocationRequest::Persist)?;
|
||||||
|
|
||||||
|
self.emit_variable_assignment(&var_name, &var_location, val)?;
|
||||||
|
var_location
|
||||||
|
}
|
||||||
Expression::Invocation(invoke_expr) => {
|
Expression::Invocation(invoke_expr) => {
|
||||||
self.expression_function_invocation(invoke_expr, scope)?;
|
self.expression_function_invocation(invoke_expr, scope)?;
|
||||||
|
|
||||||
@@ -280,6 +336,20 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
}
|
}
|
||||||
var_loc
|
var_loc
|
||||||
}
|
}
|
||||||
|
Expression::Logical(log_expr) => {
|
||||||
|
let result = self.expression_logical(log_expr, scope)?;
|
||||||
|
let var_loc = scope.add_variable(&var_name, LocationRequest::Persist)?;
|
||||||
|
|
||||||
|
// Move result from temp to new persistent variable
|
||||||
|
let result_reg = self.resolve_register(&result.location)?;
|
||||||
|
self.emit_variable_assignment(&var_name, &var_loc, result_reg)?;
|
||||||
|
|
||||||
|
// Free the temp result
|
||||||
|
if let Some(name) = result.temp_name {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
var_loc
|
||||||
|
}
|
||||||
Expression::Variable(name) => {
|
Expression::Variable(name) => {
|
||||||
let src_loc = scope.get_location_of(&name)?;
|
let src_loc = scope.get_location_of(&name)?;
|
||||||
let var_loc = scope.add_variable(&var_name, LocationRequest::Persist)?;
|
let var_loc = scope.add_variable(&var_name, LocationRequest::Persist)?;
|
||||||
@@ -317,6 +387,50 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(Some(loc))
|
Ok(Some(loc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_assignment<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: AssignmentExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let AssignmentExpression {
|
||||||
|
identifier,
|
||||||
|
expression,
|
||||||
|
} = expr;
|
||||||
|
|
||||||
|
let location = scope.get_location_of(&identifier)?;
|
||||||
|
let (val_str, cleanup) = self.compile_operand(*expression, scope)?;
|
||||||
|
|
||||||
|
let debug_tag = if self.config.debug {
|
||||||
|
format!(" #{}", identifier)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
match location {
|
||||||
|
VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => {
|
||||||
|
self.write_output(format!("move r{reg} {val_str}{debug_tag}"))?;
|
||||||
|
}
|
||||||
|
VariableLocation::Stack(offset) => {
|
||||||
|
// Calculate address: sp - offset
|
||||||
|
self.write_output(format!(
|
||||||
|
"sub r{0} sp {offset}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
// Store value to stack/db at address
|
||||||
|
self.write_output(format!(
|
||||||
|
"put db r{0} {val_str}{debug_tag}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_function_invocation(
|
fn expression_function_invocation(
|
||||||
&mut self,
|
&mut self,
|
||||||
invoke_expr: InvocationExpression,
|
invoke_expr: InvocationExpression,
|
||||||
@@ -346,6 +460,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
let num_str = num.to_string();
|
let num_str = num.to_string();
|
||||||
self.write_output(format!("push {num_str}"))?;
|
self.write_output(format!("push {num_str}"))?;
|
||||||
}
|
}
|
||||||
|
Expression::Literal(Literal::Boolean(b)) => {
|
||||||
|
let val = if b { "1" } else { "0" };
|
||||||
|
self.write_output(format!("push {val}"))?;
|
||||||
|
}
|
||||||
Expression::Variable(var_name) => match stack.get_location_of(var_name)? {
|
Expression::Variable(var_name) => match stack.get_location_of(var_name)? {
|
||||||
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => {
|
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => {
|
||||||
self.write_output(format!("push r{reg}"))?;
|
self.write_output(format!("push r{reg}"))?;
|
||||||
@@ -374,6 +492,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
stack.free_temp(name)?;
|
stack.free_temp(name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::Logical(log_expr) => {
|
||||||
|
// Compile the logical expression to a temp register
|
||||||
|
let result = self.expression_logical(log_expr, stack)?;
|
||||||
|
let reg_str = self.resolve_register(&result.location)?;
|
||||||
|
self.write_output(format!("push {reg_str}"))?;
|
||||||
|
if let Some(name) = result.temp_name {
|
||||||
|
stack.free_temp(name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::Unknown(format!(
|
return Err(Error::Unknown(format!(
|
||||||
"Attempted to call `{}` with an unsupported argument type",
|
"Attempted to call `{}` with an unsupported argument type",
|
||||||
@@ -418,6 +545,131 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_if<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: IfExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let end_label = self.next_label_name();
|
||||||
|
let else_label = if expr.else_branch.is_some() {
|
||||||
|
self.next_label_name()
|
||||||
|
} else {
|
||||||
|
end_label.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compile Condition
|
||||||
|
let (cond_str, cleanup) = self.compile_operand(*expr.condition, scope)?;
|
||||||
|
|
||||||
|
// If condition is FALSE (0), jump to else_label
|
||||||
|
self.write_output(format!("beq {cond_str} 0 {else_label}"))?;
|
||||||
|
|
||||||
|
if let Some(name) = cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile Body
|
||||||
|
// Scope variables in body are ephemeral to the block, handled by expression_block
|
||||||
|
self.expression_block(expr.body, scope)?;
|
||||||
|
|
||||||
|
// If we have an else branch, we need to jump over it after the 'if' body
|
||||||
|
if expr.else_branch.is_some() {
|
||||||
|
self.write_output(format!("j {end_label}"))?;
|
||||||
|
self.write_output(format!("{else_label}:"))?;
|
||||||
|
|
||||||
|
match *expr.else_branch.unwrap() {
|
||||||
|
Expression::Block(block) => self.expression_block(block, scope)?,
|
||||||
|
Expression::If(if_expr) => self.expression_if(if_expr, scope)?,
|
||||||
|
_ => unreachable!("Parser ensures else branch is Block or If"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_output(format!("{end_label}:"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_loop<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: LoopExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let start_label = self.next_label_name();
|
||||||
|
let end_label = self.next_label_name();
|
||||||
|
|
||||||
|
// Push labels to stack for 'break' and 'continue'
|
||||||
|
self.loop_stack
|
||||||
|
.push((start_label.clone(), end_label.clone()));
|
||||||
|
|
||||||
|
self.write_output(format!("{start_label}:"))?;
|
||||||
|
|
||||||
|
// Compile Body
|
||||||
|
self.expression_block(expr.body, scope)?;
|
||||||
|
|
||||||
|
// Jump back to start
|
||||||
|
self.write_output(format!("j {start_label}"))?;
|
||||||
|
self.write_output(format!("{end_label}:"))?;
|
||||||
|
|
||||||
|
self.loop_stack.pop();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_while<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: WhileExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let start_label = self.next_label_name();
|
||||||
|
let end_label = self.next_label_name();
|
||||||
|
|
||||||
|
// Push labels to stack for 'break' and 'continue'
|
||||||
|
self.loop_stack
|
||||||
|
.push((start_label.clone(), end_label.clone()));
|
||||||
|
|
||||||
|
self.write_output(format!("{start_label}:"))?;
|
||||||
|
|
||||||
|
// Compile Condition
|
||||||
|
let (cond_str, cleanup) = self.compile_operand(*expr.condition, scope)?;
|
||||||
|
|
||||||
|
// If condition is FALSE, jump to end
|
||||||
|
self.write_output(format!("beq {cond_str} 0 {end_label}"))?;
|
||||||
|
|
||||||
|
if let Some(name) = cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile Body
|
||||||
|
self.expression_block(expr.body, scope)?;
|
||||||
|
|
||||||
|
// Jump back to start
|
||||||
|
self.write_output(format!("j {start_label}"))?;
|
||||||
|
self.write_output(format!("{end_label}:"))?;
|
||||||
|
|
||||||
|
self.loop_stack.pop();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_break(&mut self) -> Result<(), Error> {
|
||||||
|
if let Some((_, end_label)) = self.loop_stack.last() {
|
||||||
|
self.write_output(format!("j {end_label}"))?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// This is a semantic error, but for now we can return a generic error
|
||||||
|
// Ideally we'd have a specific error type for this
|
||||||
|
Err(Error::Unknown("Break statement outside of loop".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_continue(&mut self) -> Result<(), Error> {
|
||||||
|
if let Some((start_label, _)) = self.loop_stack.last() {
|
||||||
|
self.write_output(format!("j {start_label}"))?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::Unknown("Continue statement outside of loop".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper to resolve a location to a register string (e.g., "r0").
|
/// Helper to resolve a location to a register string (e.g., "r0").
|
||||||
/// Note: This does not handle Stack locations automatically, as they require
|
/// Note: This does not handle Stack locations automatically, as they require
|
||||||
/// instruction emission to load. Use `compile_operand` for general handling.
|
/// instruction emission to load. Use `compile_operand` for general handling.
|
||||||
@@ -444,6 +696,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
return Ok((n.to_string(), None));
|
return Ok((n.to_string(), None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimization for boolean literals
|
||||||
|
if let Expression::Literal(Literal::Boolean(b)) = expr {
|
||||||
|
return Ok((if b { "1".to_string() } else { "0".to_string() }, None));
|
||||||
|
}
|
||||||
|
|
||||||
// Optimization for negated literals used as operands.
|
// Optimization for negated literals used as operands.
|
||||||
// E.g., `1 + -2` -> return "-2" string, no register used.
|
// E.g., `1 + -2` -> return "-2" string, no register used.
|
||||||
if let Expression::Negation(inner) = &expr
|
if let Expression::Negation(inner) = &expr
|
||||||
@@ -524,6 +781,73 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_logical<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: LogicalExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<CompilationResult, Error> {
|
||||||
|
match expr {
|
||||||
|
LogicalExpression::Not(inner) => {
|
||||||
|
let (inner_str, cleanup) = self.compile_operand(*inner, scope)?;
|
||||||
|
|
||||||
|
let result_name = self.next_temp_name();
|
||||||
|
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp)?;
|
||||||
|
let result_reg = self.resolve_register(&result_loc)?;
|
||||||
|
|
||||||
|
// seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0
|
||||||
|
self.write_output(format!("seq {result_reg} {inner_str} 0"))?;
|
||||||
|
|
||||||
|
if let Some(name) = cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CompilationResult {
|
||||||
|
location: result_loc,
|
||||||
|
temp_name: Some(result_name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let (op_str, left_expr, right_expr) = match expr {
|
||||||
|
LogicalExpression::And(l, r) => ("and", l, r),
|
||||||
|
LogicalExpression::Or(l, r) => ("or", l, r),
|
||||||
|
LogicalExpression::Equal(l, r) => ("seq", l, r),
|
||||||
|
LogicalExpression::NotEqual(l, r) => ("sne", l, r),
|
||||||
|
LogicalExpression::GreaterThan(l, r) => ("sgt", l, r),
|
||||||
|
LogicalExpression::GreaterThanOrEqual(l, r) => ("sge", l, r),
|
||||||
|
LogicalExpression::LessThan(l, r) => ("slt", l, r),
|
||||||
|
LogicalExpression::LessThanOrEqual(l, r) => ("sle", l, r),
|
||||||
|
LogicalExpression::Not(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compile LHS
|
||||||
|
let (lhs_str, lhs_cleanup) = self.compile_operand(*left_expr, scope)?;
|
||||||
|
// Compile RHS
|
||||||
|
let (rhs_str, rhs_cleanup) = self.compile_operand(*right_expr, scope)?;
|
||||||
|
|
||||||
|
// Allocate result register
|
||||||
|
let result_name = self.next_temp_name();
|
||||||
|
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp)?;
|
||||||
|
let result_reg = self.resolve_register(&result_loc)?;
|
||||||
|
|
||||||
|
// Emit instruction: op result lhs rhs
|
||||||
|
self.write_output(format!("{op_str} {result_reg} {lhs_str} {rhs_str}"))?;
|
||||||
|
|
||||||
|
// Clean up operand temps
|
||||||
|
if let Some(name) = lhs_cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
if let Some(name) = rhs_cleanup {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CompilationResult {
|
||||||
|
location: result_loc,
|
||||||
|
temp_name: Some(result_name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_block<'v>(
|
fn expression_block<'v>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut expr: BlockExpression,
|
mut expr: BlockExpression,
|
||||||
@@ -611,6 +935,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
num,
|
num,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
Expression::Literal(Literal::Boolean(b)) => {
|
||||||
|
let val = if b { "1" } else { "0" };
|
||||||
|
self.emit_variable_assignment(
|
||||||
|
"returnValue",
|
||||||
|
&VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
|
||||||
|
val,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
Expression::Binary(bin_expr) => {
|
Expression::Binary(bin_expr) => {
|
||||||
let result = self.expression_binary(bin_expr, scope)?;
|
let result = self.expression_binary(bin_expr, scope)?;
|
||||||
let result_reg = self.resolve_register(&result.location)?;
|
let result_reg = self.resolve_register(&result.location)?;
|
||||||
@@ -623,6 +955,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
scope.free_temp(name)?;
|
scope.free_temp(name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::Logical(log_expr) => {
|
||||||
|
let result = self.expression_logical(log_expr, scope)?;
|
||||||
|
let result_reg = self.resolve_register(&result.location)?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"move r{} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
result_reg
|
||||||
|
))?;
|
||||||
|
if let Some(name) = result.temp_name {
|
||||||
|
scope.free_temp(name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::Unknown(format!(
|
return Err(Error::Unknown(format!(
|
||||||
"Unsupported `return` statement: {:?}",
|
"Unsupported `return` statement: {:?}",
|
||||||
@@ -748,3 +1092,4 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,15 +164,21 @@ impl Parser {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if the next or current token is an operator
|
// check if the next or current token is an operator, comparison, or logical symbol
|
||||||
if self_matches_peek!(self, TokenType::Symbol(s) if s.is_operator()) {
|
if self_matches_peek!(
|
||||||
return Ok(Some(Expression::Binary(self.binary(lhs)?)));
|
self,
|
||||||
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
||||||
|
) {
|
||||||
|
return Ok(Some(self.infix(lhs)?));
|
||||||
}
|
}
|
||||||
// This is an edge case. We need to move back one token if the current token is an operator
|
// This is an edge case. We need to move back one token if the current token is an operator
|
||||||
// so the binary expression can pick up the operator
|
// so the binary expression can pick up the operator
|
||||||
else if self_matches_current!(self, TokenType::Symbol(s) if s.is_operator()) {
|
else if self_matches_current!(
|
||||||
|
self,
|
||||||
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
||||||
|
) {
|
||||||
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
||||||
return Ok(Some(Expression::Binary(self.binary(lhs)?)));
|
return Ok(Some(self.infix(lhs)?));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(lhs))
|
Ok(Some(lhs))
|
||||||
@@ -198,9 +204,7 @@ impl Parser {
|
|||||||
|
|
||||||
let expr = match current_token.token_type {
|
let expr = match current_token.token_type {
|
||||||
// match unsupported keywords
|
// match unsupported keywords
|
||||||
TokenType::Keyword(e)
|
TokenType::Keyword(e) if matches_keyword!(e, Keyword::Enum) => {
|
||||||
if matches_keyword!(e, Keyword::Enum, Keyword::If, Keyword::Else) =>
|
|
||||||
{
|
|
||||||
return Err(Error::UnsupportedKeyword(current_token.clone()));
|
return Err(Error::UnsupportedKeyword(current_token.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +216,35 @@ impl Parser {
|
|||||||
// match functions with a `fn` keyword
|
// match functions with a `fn` keyword
|
||||||
TokenType::Keyword(Keyword::Fn) => Expression::Function(self.function()?),
|
TokenType::Keyword(Keyword::Fn) => Expression::Function(self.function()?),
|
||||||
|
|
||||||
|
// match if statements
|
||||||
|
TokenType::Keyword(Keyword::If) => Expression::If(self.if_expression()?),
|
||||||
|
|
||||||
|
// match loop statements
|
||||||
|
TokenType::Keyword(Keyword::Loop) => Expression::Loop(self.loop_expression()?),
|
||||||
|
|
||||||
|
// match while statements
|
||||||
|
TokenType::Keyword(Keyword::While) => Expression::While(self.while_expression()?),
|
||||||
|
|
||||||
|
// match break statements
|
||||||
|
TokenType::Keyword(Keyword::Break) => {
|
||||||
|
// make sure the next token is a semi-colon
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
Expression::Break
|
||||||
|
}
|
||||||
|
|
||||||
|
// match continue statements
|
||||||
|
TokenType::Keyword(Keyword::Continue) => {
|
||||||
|
// make sure the next token is a semi-colon
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
Expression::Continue
|
||||||
|
}
|
||||||
|
|
||||||
// match syscalls with a `syscall` keyword
|
// match syscalls with a `syscall` keyword
|
||||||
TokenType::Identifier(ref id) if SysCall::is_syscall(id) => {
|
TokenType::Identifier(ref id) if SysCall::is_syscall(id) => {
|
||||||
Expression::Syscall(self.syscall()?)
|
Expression::Syscall(self.syscall()?)
|
||||||
@@ -238,7 +271,9 @@ impl Parser {
|
|||||||
TokenType::Symbol(Symbol::LBrace) => Expression::Block(self.block()?),
|
TokenType::Symbol(Symbol::LBrace) => Expression::Block(self.block()?),
|
||||||
|
|
||||||
// match literal expressions with a semi-colon afterwards
|
// match literal expressions with a semi-colon afterwards
|
||||||
TokenType::Number(_) | TokenType::String(_) => Expression::Literal(self.literal()?),
|
TokenType::Number(_) | TokenType::String(_) | TokenType::Boolean(_) => {
|
||||||
|
Expression::Literal(self.literal()?)
|
||||||
|
}
|
||||||
|
|
||||||
// match priority expressions with a left parenthesis
|
// match priority expressions with a left parenthesis
|
||||||
TokenType::Symbol(Symbol::LParen) => Expression::Priority(self.priority()?),
|
TokenType::Symbol(Symbol::LParen) => Expression::Priority(self.priority()?),
|
||||||
@@ -254,6 +289,13 @@ impl Parser {
|
|||||||
Expression::Negation(boxed!(inner_expr))
|
Expression::Negation(boxed!(inner_expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match logical NOT `!`
|
||||||
|
TokenType::Symbol(Symbol::LogicalNot) => {
|
||||||
|
self.assign_next()?; // consume the `!` symbol
|
||||||
|
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
Expression::Logical(LogicalExpression::Not(boxed!(inner_expr)))
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedToken(current_token.clone()));
|
return Err(Error::UnexpectedToken(current_token.clone()));
|
||||||
}
|
}
|
||||||
@@ -262,12 +304,12 @@ impl Parser {
|
|||||||
Ok(Some(expr))
|
Ok(Some(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_binary_child_node(&mut self) -> Result<tree_node::Expression, Error> {
|
fn get_infix_child_node(&mut self) -> Result<tree_node::Expression, Error> {
|
||||||
let current_token = token_from_option!(self.current_token);
|
let current_token = token_from_option!(self.current_token);
|
||||||
|
|
||||||
match current_token.token_type {
|
match current_token.token_type {
|
||||||
// A literal number
|
// A literal number or boolean
|
||||||
TokenType::Number(_) => self.literal().map(Expression::Literal),
|
TokenType::Number(_) | TokenType::Boolean(_) => self.literal().map(Expression::Literal),
|
||||||
// A plain variable
|
// A plain variable
|
||||||
TokenType::Identifier(ident)
|
TokenType::Identifier(ident)
|
||||||
if !self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
if !self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
||||||
@@ -286,9 +328,15 @@ impl Parser {
|
|||||||
TokenType::Symbol(Symbol::Minus) => {
|
TokenType::Symbol(Symbol::Minus) => {
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
// recurse to handle double negation or simple negation of atoms
|
// recurse to handle double negation or simple negation of atoms
|
||||||
let inner = self.get_binary_child_node()?;
|
let inner = self.get_infix_child_node()?;
|
||||||
Ok(Expression::Negation(boxed!(inner)))
|
Ok(Expression::Negation(boxed!(inner)))
|
||||||
}
|
}
|
||||||
|
// Handle Logical Not
|
||||||
|
TokenType::Symbol(Symbol::LogicalNot) => {
|
||||||
|
self.assign_next()?;
|
||||||
|
let inner = self.get_infix_child_node()?;
|
||||||
|
Ok(Expression::Logical(LogicalExpression::Not(boxed!(inner))))
|
||||||
|
}
|
||||||
_ => Err(Error::UnexpectedToken(current_token.clone())),
|
_ => Err(Error::UnexpectedToken(current_token.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,8 +393,8 @@ impl Parser {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles mathmatical expressions in the explicit order of PEMDAS
|
/// Handles mathmatical and logical expressions in the explicit order of operations
|
||||||
fn binary(&mut self, previous: Expression) -> Result<BinaryExpression, Error> {
|
fn infix(&mut self, previous: Expression) -> Result<Expression, Error> {
|
||||||
// We cannot use recursion here, as we need to handle the precedence of the operators
|
// We cannot use recursion here, as we need to handle the precedence of the operators
|
||||||
// We need to use a loop to parse the binary expressions.
|
// We need to use a loop to parse the binary expressions.
|
||||||
|
|
||||||
@@ -354,15 +402,18 @@ impl Parser {
|
|||||||
|
|
||||||
// first, make sure the previous expression supports binary expressions
|
// first, make sure the previous expression supports binary expressions
|
||||||
match previous {
|
match previous {
|
||||||
Expression::Binary(_) // 1 + 2 + 3
|
Expression::Binary(_)
|
||||||
| Expression::Invocation(_) // add() + 3
|
| Expression::Logical(_)
|
||||||
| Expression::Priority(_) // (1 + 2) + 3
|
| Expression::Invocation(_)
|
||||||
| Expression::Literal(Literal::Number(_)) // 1 + 2 (no addition of strings)
|
| Expression::Priority(_)
|
||||||
| Expression::Variable(_) // x + 2
|
| Expression::Literal(_)
|
||||||
| Expression::Negation(_) // -1 + 2
|
| Expression::Variable(_)
|
||||||
=> {}
|
| Expression::Negation(_) => {}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::InvalidSyntax(current_token.clone(), String::from("Invalid expression for binary operation")))
|
return Err(Error::InvalidSyntax(
|
||||||
|
current_token.clone(),
|
||||||
|
String::from("Invalid expression for binary/logical operation"),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,12 +423,15 @@ impl Parser {
|
|||||||
let mut operators = Vec::<Symbol>::new(); // +, +
|
let mut operators = Vec::<Symbol>::new(); // +, +
|
||||||
|
|
||||||
// build the expressions and operators vectors
|
// build the expressions and operators vectors
|
||||||
while token_matches!(current_token, TokenType::Symbol(s) if s.is_operator()) {
|
while token_matches!(
|
||||||
// We are guaranteed to have an operator symbol here as we checked in the while loop
|
current_token,
|
||||||
|
TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical()
|
||||||
|
) {
|
||||||
|
// We are guaranteed to have an operator/comparison/logical symbol here as we checked in the while loop
|
||||||
let operator = extract_token_data!(current_token, TokenType::Symbol(s), s);
|
let operator = extract_token_data!(current_token, TokenType::Symbol(s), s);
|
||||||
operators.push(operator);
|
operators.push(operator);
|
||||||
self.assign_next()?;
|
self.assign_next()?;
|
||||||
expressions.push(self.get_binary_child_node()?);
|
expressions.push(self.get_infix_child_node()?);
|
||||||
|
|
||||||
current_token = token_from_option!(self.get_next()?).clone();
|
current_token = token_from_option!(self.get_next()?).clone();
|
||||||
}
|
}
|
||||||
@@ -394,7 +448,7 @@ impl Parser {
|
|||||||
// This means that we need to keep track of the current iteration to ensure we are
|
// This means that we need to keep track of the current iteration to ensure we are
|
||||||
// removing the correct expressions from the vector
|
// removing the correct expressions from the vector
|
||||||
|
|
||||||
// Loop through operators, and build the binary expressions for exponential operators only
|
// --- PRECEDENCE LEVEL 1: Exponent (**) ---
|
||||||
for (i, operator) in operators.iter().enumerate().rev() {
|
for (i, operator) in operators.iter().enumerate().rev() {
|
||||||
if operator == &Symbol::Exp {
|
if operator == &Symbol::Exp {
|
||||||
let right = expressions.remove(i + 1);
|
let right = expressions.remove(i + 1);
|
||||||
@@ -405,12 +459,10 @@ impl Parser {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all the exponential operators from the operators vector
|
|
||||||
operators.retain(|symbol| symbol != &Symbol::Exp);
|
operators.retain(|symbol| symbol != &Symbol::Exp);
|
||||||
let mut current_iteration = 0;
|
|
||||||
|
|
||||||
// Loop through operators, and build the binary expressions for multiplication and division operators
|
// --- PRECEDENCE LEVEL 2: Multiplicative (*, /, %) ---
|
||||||
|
let mut current_iteration = 0;
|
||||||
for (i, operator) in operators.iter().enumerate() {
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
if matches!(operator, Symbol::Slash | Symbol::Asterisk | Symbol::Percent) {
|
if matches!(operator, Symbol::Slash | Symbol::Asterisk | Symbol::Percent) {
|
||||||
let index = i - current_iteration;
|
let index = i - current_iteration;
|
||||||
@@ -430,21 +482,18 @@ impl Parser {
|
|||||||
index,
|
index,
|
||||||
Expression::Binary(BinaryExpression::Modulo(boxed!(left), boxed!(right))),
|
Expression::Binary(BinaryExpression::Modulo(boxed!(left), boxed!(right))),
|
||||||
),
|
),
|
||||||
// safety: we have already checked for the operator
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
current_iteration += 1;
|
current_iteration += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all the multiplication and division operators from the operators vector
|
|
||||||
operators
|
operators
|
||||||
.retain(|symbol| !matches!(symbol, Symbol::Asterisk | Symbol::Percent | Symbol::Slash));
|
.retain(|symbol| !matches!(symbol, Symbol::Asterisk | Symbol::Percent | Symbol::Slash));
|
||||||
current_iteration = 0;
|
|
||||||
|
|
||||||
// Loop through operators, and build the binary expressions for addition and subtraction operators
|
// --- PRECEDENCE LEVEL 3: Additive (+, -) ---
|
||||||
|
current_iteration = 0;
|
||||||
for (i, operator) in operators.iter().enumerate() {
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
if operator == &Symbol::Plus || operator == &Symbol::Minus {
|
if matches!(operator, Symbol::Plus | Symbol::Minus) {
|
||||||
let index = i - current_iteration;
|
let index = i - current_iteration;
|
||||||
let left = expressions.remove(index);
|
let left = expressions.remove(index);
|
||||||
let right = expressions.remove(index);
|
let right = expressions.remove(index);
|
||||||
@@ -458,16 +507,120 @@ impl Parser {
|
|||||||
index,
|
index,
|
||||||
Expression::Binary(BinaryExpression::Subtract(boxed!(left), boxed!(right))),
|
Expression::Binary(BinaryExpression::Subtract(boxed!(left), boxed!(right))),
|
||||||
),
|
),
|
||||||
// safety: we have already checked for the operator
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
current_iteration += 1;
|
current_iteration += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all the addition and subtraction operators from the operators vector
|
|
||||||
operators.retain(|symbol| !matches!(symbol, Symbol::Plus | Symbol::Minus));
|
operators.retain(|symbol| !matches!(symbol, Symbol::Plus | Symbol::Minus));
|
||||||
|
|
||||||
|
// --- PRECEDENCE LEVEL 4: Comparison (<, >, <=, >=) ---
|
||||||
|
current_iteration = 0;
|
||||||
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
|
if operator.is_comparison() && !matches!(operator, Symbol::Equal | Symbol::NotEqual) {
|
||||||
|
let index = i - current_iteration;
|
||||||
|
let left = expressions.remove(index);
|
||||||
|
let right = expressions.remove(index);
|
||||||
|
|
||||||
|
match operator {
|
||||||
|
Symbol::LessThan => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::LessThan(
|
||||||
|
boxed!(left),
|
||||||
|
boxed!(right),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Symbol::GreaterThan => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::GreaterThan(
|
||||||
|
boxed!(left),
|
||||||
|
boxed!(right),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Symbol::LessThanOrEqual => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::LessThanOrEqual(
|
||||||
|
boxed!(left),
|
||||||
|
boxed!(right),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Symbol::GreaterThanOrEqual => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::GreaterThanOrEqual(
|
||||||
|
boxed!(left),
|
||||||
|
boxed!(right),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
current_iteration += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators.retain(|symbol| {
|
||||||
|
!symbol.is_comparison() || matches!(symbol, Symbol::Equal | Symbol::NotEqual)
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- PRECEDENCE LEVEL 5: Equality (==, !=) ---
|
||||||
|
current_iteration = 0;
|
||||||
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
|
if matches!(operator, Symbol::Equal | Symbol::NotEqual) {
|
||||||
|
let index = i - current_iteration;
|
||||||
|
let left = expressions.remove(index);
|
||||||
|
let right = expressions.remove(index);
|
||||||
|
|
||||||
|
match operator {
|
||||||
|
Symbol::Equal => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::Equal(boxed!(left), boxed!(right))),
|
||||||
|
),
|
||||||
|
Symbol::NotEqual => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::NotEqual(
|
||||||
|
boxed!(left),
|
||||||
|
boxed!(right),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
current_iteration += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators.retain(|symbol| !matches!(symbol, Symbol::Equal | Symbol::NotEqual));
|
||||||
|
|
||||||
|
// --- PRECEDENCE LEVEL 6: Logical AND (&&) ---
|
||||||
|
current_iteration = 0;
|
||||||
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
|
if matches!(operator, Symbol::LogicalAnd) {
|
||||||
|
let index = i - current_iteration;
|
||||||
|
let left = expressions.remove(index);
|
||||||
|
let right = expressions.remove(index);
|
||||||
|
|
||||||
|
expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::And(boxed!(left), boxed!(right))),
|
||||||
|
);
|
||||||
|
current_iteration += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators.retain(|symbol| !matches!(symbol, Symbol::LogicalAnd));
|
||||||
|
|
||||||
|
// --- PRECEDENCE LEVEL 7: Logical OR (||) ---
|
||||||
|
current_iteration = 0;
|
||||||
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
|
if matches!(operator, Symbol::LogicalOr) {
|
||||||
|
let index = i - current_iteration;
|
||||||
|
let left = expressions.remove(index);
|
||||||
|
let right = expressions.remove(index);
|
||||||
|
|
||||||
|
expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Logical(LogicalExpression::Or(boxed!(left), boxed!(right))),
|
||||||
|
);
|
||||||
|
current_iteration += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators.retain(|symbol| !matches!(symbol, Symbol::LogicalOr));
|
||||||
|
|
||||||
// Ensure there is only one expression left in the expressions vector, and no operators left
|
// Ensure there is only one expression left in the expressions vector, and no operators left
|
||||||
if expressions.len() != 1 || !operators.is_empty() {
|
if expressions.len() != 1 || !operators.is_empty() {
|
||||||
return Err(Error::InvalidSyntax(
|
return Err(Error::InvalidSyntax(
|
||||||
@@ -484,11 +637,7 @@ impl Parser {
|
|||||||
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the last expression is a binary expression
|
Ok(expressions.pop().unwrap())
|
||||||
match expressions.pop().unwrap() {
|
|
||||||
Expression::Binary(binary) => Ok(binary),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn priority(&mut self) -> Result<Box<Expression>, Error> {
|
fn priority(&mut self) -> Result<Box<Expression>, Error> {
|
||||||
@@ -589,10 +738,19 @@ impl Parser {
|
|||||||
let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
let return_expr = Expression::Return(boxed!(expression));
|
let return_expr = Expression::Return(boxed!(expression));
|
||||||
expressions.push(return_expr);
|
expressions.push(return_expr);
|
||||||
self.assign_next()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assign_next()?;
|
// check for semicolon
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for right brace
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::RBrace)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(BlockExpression(expressions))
|
Ok(BlockExpression(expressions))
|
||||||
}
|
}
|
||||||
@@ -634,12 +792,127 @@ impl Parser {
|
|||||||
let literal = match current_token.token_type {
|
let literal = match current_token.token_type {
|
||||||
TokenType::Number(num) => Literal::Number(num),
|
TokenType::Number(num) => Literal::Number(num),
|
||||||
TokenType::String(string) => Literal::String(string),
|
TokenType::String(string) => Literal::String(string),
|
||||||
|
TokenType::Boolean(boolean) => Literal::Boolean(boolean),
|
||||||
_ => return Err(Error::UnexpectedToken(current_token.clone())),
|
_ => return Err(Error::UnexpectedToken(current_token.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(literal)
|
Ok(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn if_expression(&mut self) -> Result<IfExpression, Error> {
|
||||||
|
let current_token = token_from_option!(self.current_token);
|
||||||
|
if !self_matches_current!(self, TokenType::Keyword(Keyword::If)) {
|
||||||
|
return Err(Error::UnexpectedToken(current_token.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume 'if'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
self.assign_next()?;
|
||||||
|
|
||||||
|
// parse condition
|
||||||
|
let condition = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
|
// check for ')'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for '{'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse body
|
||||||
|
let body = self.block()?;
|
||||||
|
|
||||||
|
// check for 'else'
|
||||||
|
let else_branch = if self_matches_peek!(self, TokenType::Keyword(Keyword::Else)) {
|
||||||
|
self.assign_next()?; // consume 'else'
|
||||||
|
|
||||||
|
if self_matches_peek!(self, TokenType::Keyword(Keyword::If)) {
|
||||||
|
// else if ...
|
||||||
|
self.assign_next()?;
|
||||||
|
Some(boxed!(Expression::If(self.if_expression()?)))
|
||||||
|
} else if self_matches_peek!(self, TokenType::Symbol(Symbol::LBrace)) {
|
||||||
|
// else { ... }
|
||||||
|
self.assign_next()?;
|
||||||
|
Some(boxed!(Expression::Block(self.block()?)))
|
||||||
|
} else {
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
|
token_from_option!(self.get_next()?).clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(IfExpression {
|
||||||
|
condition: boxed!(condition),
|
||||||
|
body,
|
||||||
|
else_branch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_expression(&mut self) -> Result<LoopExpression, Error> {
|
||||||
|
let current_token = token_from_option!(self.current_token);
|
||||||
|
if !self_matches_current!(self, TokenType::Keyword(Keyword::Loop)) {
|
||||||
|
return Err(Error::UnexpectedToken(current_token.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for '{'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse body
|
||||||
|
let body = self.block()?;
|
||||||
|
|
||||||
|
Ok(LoopExpression { body })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn while_expression(&mut self) -> Result<WhileExpression, Error> {
|
||||||
|
let current_token = token_from_option!(self.current_token);
|
||||||
|
if !self_matches_current!(self, TokenType::Keyword(Keyword::While)) {
|
||||||
|
return Err(Error::UnexpectedToken(current_token.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume 'while'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
self.assign_next()?;
|
||||||
|
|
||||||
|
// parse condition
|
||||||
|
let condition = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
|
// check for ')'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for '{'
|
||||||
|
let next = token_from_option!(self.get_next()?);
|
||||||
|
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
|
||||||
|
return Err(Error::UnexpectedToken(next.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse body
|
||||||
|
let body = self.block()?;
|
||||||
|
|
||||||
|
Ok(WhileExpression {
|
||||||
|
condition: boxed!(condition),
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn function(&mut self) -> Result<FunctionExpression, Error> {
|
fn function(&mut self) -> Result<FunctionExpression, Error> {
|
||||||
let current_token = token_from_option!(self.current_token);
|
let current_token = token_from_option!(self.current_token);
|
||||||
// Sanify check that the current token is a `fn` keyword
|
// Sanify check that the current token is a `fn` keyword
|
||||||
@@ -929,3 +1202,4 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use tokenizer::token::Number;
|
|||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
String(String),
|
String(String),
|
||||||
|
Boolean(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Literal {
|
impl std::fmt::Display for Literal {
|
||||||
@@ -12,6 +13,7 @@ impl std::fmt::Display for Literal {
|
|||||||
match self {
|
match self {
|
||||||
Literal::Number(n) => write!(f, "{}", n),
|
Literal::Number(n) => write!(f, "{}", n),
|
||||||
Literal::String(s) => write!(f, "\"{}\"", s),
|
Literal::String(s) => write!(f, "\"{}\"", s),
|
||||||
|
Literal::Boolean(b) => write!(f, "{}", if *b { 1 } else { 0 }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,41 +168,91 @@ impl std::fmt::Display for DeviceDeclarationExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct IfExpression {
|
||||||
|
pub condition: Box<Expression>,
|
||||||
|
pub body: BlockExpression,
|
||||||
|
pub else_branch: Option<Box<Expression>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for IfExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(if ({}) {}", self.condition, self.body)?;
|
||||||
|
if let Some(else_branch) = &self.else_branch {
|
||||||
|
write!(f, " else {}", else_branch)?;
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct LoopExpression {
|
||||||
|
pub body: BlockExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for LoopExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(loop {})", self.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct WhileExpression {
|
||||||
|
pub condition: Box<Expression>,
|
||||||
|
pub body: BlockExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for WhileExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(while {} {})", self.condition, self.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Assignment(AssignmentExpression),
|
Assignment(AssignmentExpression),
|
||||||
Binary(BinaryExpression),
|
Binary(BinaryExpression),
|
||||||
Block(BlockExpression),
|
Block(BlockExpression),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
Declaration(String, Box<Expression>),
|
Declaration(String, Box<Expression>),
|
||||||
|
DeviceDeclaration(DeviceDeclarationExpression),
|
||||||
Function(FunctionExpression),
|
Function(FunctionExpression),
|
||||||
|
If(IfExpression),
|
||||||
Invocation(InvocationExpression),
|
Invocation(InvocationExpression),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
Logical(LogicalExpression),
|
Logical(LogicalExpression),
|
||||||
|
Loop(LoopExpression),
|
||||||
Negation(Box<Expression>),
|
Negation(Box<Expression>),
|
||||||
Priority(Box<Expression>),
|
Priority(Box<Expression>),
|
||||||
Return(Box<Expression>),
|
Return(Box<Expression>),
|
||||||
Variable(String),
|
|
||||||
DeviceDeclaration(DeviceDeclarationExpression),
|
|
||||||
Syscall(SysCall),
|
Syscall(SysCall),
|
||||||
|
Variable(String),
|
||||||
|
While(WhileExpression),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Expression {
|
impl std::fmt::Display for Expression {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Expression::Literal(l) => write!(f, "{}", l),
|
|
||||||
Expression::Negation(e) => write!(f, "(-{})", e),
|
|
||||||
Expression::Binary(e) => write!(f, "{}", e),
|
|
||||||
Expression::Logical(e) => write!(f, "{}", e),
|
|
||||||
Expression::Assignment(e) => write!(f, "{}", e),
|
Expression::Assignment(e) => write!(f, "{}", e),
|
||||||
Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e),
|
Expression::Binary(e) => write!(f, "{}", e),
|
||||||
Expression::Function(e) => write!(f, "{}", e),
|
|
||||||
Expression::Block(e) => write!(f, "{}", e),
|
Expression::Block(e) => write!(f, "{}", e),
|
||||||
|
Expression::Break => write!(f, "break"),
|
||||||
|
Expression::Continue => write!(f, "continue"),
|
||||||
|
Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e),
|
||||||
|
Expression::DeviceDeclaration(e) => write!(f, "{}", e),
|
||||||
|
Expression::Function(e) => write!(f, "{}", e),
|
||||||
|
Expression::If(e) => write!(f, "{}", e),
|
||||||
Expression::Invocation(e) => write!(f, "{}", e),
|
Expression::Invocation(e) => write!(f, "{}", e),
|
||||||
Expression::Variable(id) => write!(f, "{}", id),
|
Expression::Literal(l) => write!(f, "{}", l),
|
||||||
|
Expression::Logical(e) => write!(f, "{}", e),
|
||||||
|
Expression::Loop(e) => write!(f, "{}", e),
|
||||||
|
Expression::Negation(e) => write!(f, "(-{})", e),
|
||||||
Expression::Priority(e) => write!(f, "({})", e),
|
Expression::Priority(e) => write!(f, "({})", e),
|
||||||
Expression::Return(e) => write!(f, "(return {})", e),
|
Expression::Return(e) => write!(f, "(return {})", e),
|
||||||
Expression::DeviceDeclaration(e) => write!(f, "{}", e),
|
|
||||||
Expression::Syscall(e) => write!(f, "{}", e),
|
Expression::Syscall(e) => write!(f, "{}", e),
|
||||||
|
Expression::Variable(id) => write!(f, "{}", id),
|
||||||
|
Expression::While(e) => write!(f, "{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,6 +409,8 @@ impl Tokenizer {
|
|||||||
"device" if next_ws!() => keyword!(Device),
|
"device" if next_ws!() => keyword!(Device),
|
||||||
"loop" if next_ws!() => keyword!(Loop),
|
"loop" if next_ws!() => keyword!(Loop),
|
||||||
"break" if next_ws!() => keyword!(Break),
|
"break" if next_ws!() => keyword!(Break),
|
||||||
|
"while" if next_ws!() => keyword!(While),
|
||||||
|
"continue" if next_ws!() => keyword!(Continue),
|
||||||
|
|
||||||
// boolean literals
|
// boolean literals
|
||||||
"true" if next_ws!() => {
|
"true" if next_ws!() => {
|
||||||
@@ -886,4 +888,39 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compact_syntax() -> Result<()> {
|
||||||
|
let mut tokenizer = Tokenizer::from(String::from("if(true) while(false)"));
|
||||||
|
|
||||||
|
// if(true)
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Keyword(Keyword::If)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Symbol(Symbol::LParen)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Boolean(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Symbol(Symbol::RParen)
|
||||||
|
);
|
||||||
|
|
||||||
|
// while(false)
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Keyword(Keyword::While)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.next_token()?.unwrap().token_type,
|
||||||
|
TokenType::Symbol(Symbol::LParen)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ impl Symbol {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
|
||||||
pub enum Keyword {
|
pub enum Keyword {
|
||||||
|
/// Represents the `continue` keyword
|
||||||
|
Continue,
|
||||||
/// Represents the `let` keyword
|
/// Represents the `let` keyword
|
||||||
Let,
|
Let,
|
||||||
/// Represents the `fn` keyword
|
/// Represents the `fn` keyword
|
||||||
@@ -228,4 +230,6 @@ pub enum Keyword {
|
|||||||
Loop,
|
Loop,
|
||||||
/// Represents the `break` keyword
|
/// Represents the `break` keyword
|
||||||
Break,
|
Break,
|
||||||
|
/// Represents the `while` keyword
|
||||||
|
While,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user