v1 constant expressions
This commit is contained in:
@@ -4,10 +4,10 @@ use parser::{
|
|||||||
Parser as ASTParser,
|
Parser as ASTParser,
|
||||||
sys_call::{SysCall, System},
|
sys_call::{SysCall, System},
|
||||||
tree_node::{
|
tree_node::{
|
||||||
AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression,
|
AssignmentExpression, BinaryExpression, BlockExpression, ConstDeclarationExpression,
|
||||||
Expression, FunctionExpression, IfExpression, InvocationExpression, Literal,
|
DeviceDeclarationExpression, Expression, FunctionExpression, IfExpression,
|
||||||
LiteralOrVariable, LogicalExpression, LoopExpression, MemberAccessExpression, Span,
|
InvocationExpression, Literal, LiteralOrVariable, LogicalExpression, LoopExpression,
|
||||||
Spanned, WhileExpression,
|
MemberAccessExpression, Span, Spanned, WhileExpression,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
@@ -34,6 +34,20 @@ macro_rules! debug {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_literal(literal: Literal, allow_strings: bool) -> Result<String, Error> {
|
||||||
|
if !allow_strings && matches!(literal, Literal::String(_)) {
|
||||||
|
return Err(Error::Unknown(
|
||||||
|
"Literal strings are not allowed in this context".to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(match literal {
|
||||||
|
Literal::String(s) => s,
|
||||||
|
Literal::Number(n) => n.to_string(),
|
||||||
|
Literal::Boolean(b) => if b { "1" } else { "0" }.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -58,6 +72,9 @@ quick_error! {
|
|||||||
AgrumentMismatch(func_name: String, span: Span) {
|
AgrumentMismatch(func_name: String, span: Span) {
|
||||||
display("Incorrect number of arguments passed into `{func_name}`")
|
display("Incorrect number of arguments passed into `{func_name}`")
|
||||||
}
|
}
|
||||||
|
ConstAssignment(ident: String, span: Span) {
|
||||||
|
display("Attempted to re-assign a value to const variable `{ident}`")
|
||||||
|
}
|
||||||
Unknown(reason: String, span: Option<Span>) {
|
Unknown(reason: String, span: Option<Span>) {
|
||||||
display("{reason}")
|
display("{reason}")
|
||||||
}
|
}
|
||||||
@@ -84,6 +101,7 @@ impl From<Error> for lsp_types::Diagnostic {
|
|||||||
DuplicateIdentifier(_, span)
|
DuplicateIdentifier(_, span)
|
||||||
| UnknownIdentifier(_, span)
|
| UnknownIdentifier(_, span)
|
||||||
| InvalidDevice(_, span)
|
| InvalidDevice(_, span)
|
||||||
|
| ConstAssignment(_, span)
|
||||||
| AgrumentMismatch(_, span) => Diagnostic {
|
| AgrumentMismatch(_, span) => Diagnostic {
|
||||||
range: span.into(),
|
range: span.into(),
|
||||||
message: value.to_string(),
|
message: value.to_string(),
|
||||||
@@ -267,6 +285,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
// decl_expr is Box<Spanned<Expression>>
|
// decl_expr is Box<Spanned<Expression>>
|
||||||
self.expression_declaration(var_name, *decl_expr, scope)
|
self.expression_declaration(var_name, *decl_expr, scope)
|
||||||
}
|
}
|
||||||
|
Expression::ConstDeclaration(const_decl_expr) => {
|
||||||
|
self.expression_const_declaration(const_decl_expr.node, scope)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
Expression::Assignment(assign_expr) => {
|
Expression::Assignment(assign_expr) => {
|
||||||
self.expression_assignment(assign_expr.node, scope)?;
|
self.expression_assignment(assign_expr.node, scope)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@@ -435,6 +457,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
VariableLocation::Stack(_) => {
|
VariableLocation::Stack(_) => {
|
||||||
self.write_output(format!("push {}{debug_tag}", source_value.into()))?;
|
self.write_output(format!("push {}{debug_tag}", source_value.into()))?;
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(_) => {
|
||||||
|
return Err(Error::Unknown(
|
||||||
|
r#"Attempted to emit a variable assignent for a constant value.
|
||||||
|
This is a Compiler bug and should be reported to the developer."#
|
||||||
|
.into(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -581,6 +611,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
))?;
|
))?;
|
||||||
format!("r{}", VariableScope::TEMP_STACK_REGISTER)
|
format!("r{}", VariableScope::TEMP_STACK_REGISTER)
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
self.emit_variable_assignment(&name_str, &var_loc, src_str)?;
|
self.emit_variable_assignment(&name_str, &var_loc, src_str)?;
|
||||||
(var_loc, None)
|
(var_loc, None)
|
||||||
@@ -638,6 +669,22 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_const_declaration<'v>(
|
||||||
|
&mut self,
|
||||||
|
expr: ConstDeclarationExpression,
|
||||||
|
scope: &mut VariableScope<'v>,
|
||||||
|
) -> Result<CompilationResult, Error> {
|
||||||
|
let ConstDeclarationExpression {
|
||||||
|
name: const_name,
|
||||||
|
value: const_value,
|
||||||
|
} = expr;
|
||||||
|
|
||||||
|
Ok(CompilationResult {
|
||||||
|
location: scope.define_const(const_name.node, const_value.node)?,
|
||||||
|
temp_name: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_assignment<'v>(
|
fn expression_assignment<'v>(
|
||||||
&mut self,
|
&mut self,
|
||||||
expr: AssignmentExpression,
|
expr: AssignmentExpression,
|
||||||
@@ -685,6 +732,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
VariableScope::TEMP_STACK_REGISTER
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(_) => {
|
||||||
|
return Err(Error::ConstAssignment(identifier.node, identifier.span));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = cleanup {
|
if let Some(name) = cleanup {
|
||||||
@@ -782,6 +832,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
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}"))?;
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(lit) => {
|
||||||
|
self.write_output(format!("push {}", extract_literal(lit, false)?))?;
|
||||||
|
}
|
||||||
VariableLocation::Stack(stack_offset) => {
|
VariableLocation::Stack(stack_offset) => {
|
||||||
self.write_output(format!(
|
self.write_output(format!(
|
||||||
"sub r{0} sp {stack_offset}",
|
"sub r{0} sp {stack_offset}",
|
||||||
@@ -1041,6 +1094,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
fn resolve_register(&self, loc: &VariableLocation) -> Result<String, Error> {
|
fn resolve_register(&self, loc: &VariableLocation) -> Result<String, Error> {
|
||||||
match loc {
|
match loc {
|
||||||
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")),
|
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")),
|
||||||
|
VariableLocation::Constant(_) => Err(Error::Unknown(
|
||||||
|
"Cannot resolve a constant value to register".into(),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
VariableLocation::Stack(_) => Err(Error::Unknown(
|
VariableLocation::Stack(_) => Err(Error::Unknown(
|
||||||
"Cannot resolve Stack location directly to register string without context".into(),
|
"Cannot resolve Stack location directly to register string without context".into(),
|
||||||
None,
|
None,
|
||||||
@@ -1090,6 +1147,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => {
|
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => {
|
||||||
Ok((format!("r{r}"), result.temp_name))
|
Ok((format!("r{r}"), result.temp_name))
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(lit) => match lit {
|
||||||
|
Literal::Number(n) => Ok((n.to_string(), None)),
|
||||||
|
Literal::Boolean(b) => Ok((if b { "1" } else { "0" }.to_string(), None)),
|
||||||
|
Literal::String(s) => Ok((s, None)),
|
||||||
|
},
|
||||||
VariableLocation::Stack(offset) => {
|
VariableLocation::Stack(offset) => {
|
||||||
// If it's on the stack, we must load it into a temp to use it as an operand
|
// If it's on the stack, we must load it into a temp to use it as an operand
|
||||||
let temp_name = self.next_temp_name();
|
let temp_name = self.next_temp_name();
|
||||||
@@ -1256,11 +1318,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// First, sort the expressions to ensure functions are hoisted
|
// First, sort the expressions to ensure functions are hoisted
|
||||||
expr.0.sort_by(|a, b| {
|
expr.0.sort_by(|a, b| {
|
||||||
if matches!(b.node, Expression::Function(_))
|
if matches!(
|
||||||
&& matches!(a.node, Expression::Function(_))
|
b.node,
|
||||||
{
|
Expression::Function(_) | Expression::ConstDeclaration(_)
|
||||||
|
) && matches!(
|
||||||
|
a.node,
|
||||||
|
Expression::Function(_) | Expression::ConstDeclaration(_)
|
||||||
|
) {
|
||||||
std::cmp::Ordering::Equal
|
std::cmp::Ordering::Equal
|
||||||
} else if matches!(a.node, Expression::Function(_)) {
|
} else if matches!(
|
||||||
|
a.node,
|
||||||
|
Expression::Function(_) | Expression::ConstDeclaration(_)
|
||||||
|
) {
|
||||||
std::cmp::Ordering::Less
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
std::cmp::Ordering::Greater
|
std::cmp::Ordering::Greater
|
||||||
@@ -1332,6 +1401,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
debug!(self, "#returnValue")
|
debug!(self, "#returnValue")
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
VariableLocation::Constant(lit) => {
|
||||||
|
let str = extract_literal(lit, false)?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"move r{} {str} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
debug!(self, "#returnValue")
|
||||||
|
))?
|
||||||
|
}
|
||||||
VariableLocation::Stack(offset) => {
|
VariableLocation::Stack(offset) => {
|
||||||
self.write_output(format!(
|
self.write_output(format!(
|
||||||
"sub r{} sp {offset}",
|
"sub r{} sp {offset}",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// r1 - r7 : Temporary Variables
|
// r1 - r7 : Temporary Variables
|
||||||
// r8 - r14 : Persistant Variables
|
// r8 - r14 : Persistant Variables
|
||||||
|
|
||||||
|
use parser::tree_node::Literal;
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
@@ -43,11 +44,14 @@ pub enum VariableLocation {
|
|||||||
Persistant(u8),
|
Persistant(u8),
|
||||||
/// Represents a a stack offset (current stack - offset = variable loc)
|
/// Represents a a stack offset (current stack - offset = variable loc)
|
||||||
Stack(u16),
|
Stack(u16),
|
||||||
|
/// Represents a constant value and should be directly substituted as such.
|
||||||
|
Constant(Literal),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VariableScope<'a> {
|
pub struct VariableScope<'a> {
|
||||||
temporary_vars: VecDeque<u8>,
|
temporary_vars: VecDeque<u8>,
|
||||||
persistant_vars: VecDeque<u8>,
|
persistant_vars: VecDeque<u8>,
|
||||||
|
constant_vars: HashMap<String, Literal>,
|
||||||
var_lookup_table: HashMap<String, VariableLocation>,
|
var_lookup_table: HashMap<String, VariableLocation>,
|
||||||
stack_offset: u16,
|
stack_offset: u16,
|
||||||
parent: Option<&'a VariableScope<'a>>,
|
parent: Option<&'a VariableScope<'a>>,
|
||||||
@@ -61,6 +65,7 @@ impl<'a> Default for VariableScope<'a> {
|
|||||||
persistant_vars: PERSIST.to_vec().into(),
|
persistant_vars: PERSIST.to_vec().into(),
|
||||||
temporary_vars: TEMP.to_vec().into(),
|
temporary_vars: TEMP.to_vec().into(),
|
||||||
var_lookup_table: HashMap::new(),
|
var_lookup_table: HashMap::new(),
|
||||||
|
constant_vars: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,6 +98,7 @@ impl<'a> VariableScope<'a> {
|
|||||||
parent: Option::Some(parent),
|
parent: Option::Some(parent),
|
||||||
temporary_vars: parent.temporary_vars.clone(),
|
temporary_vars: parent.temporary_vars.clone(),
|
||||||
persistant_vars: parent.persistant_vars.clone(),
|
persistant_vars: parent.persistant_vars.clone(),
|
||||||
|
constant_vars: parent.constant_vars.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,6 +148,20 @@ impl<'a> VariableScope<'a> {
|
|||||||
Ok(var_location)
|
Ok(var_location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn define_const(
|
||||||
|
&mut self,
|
||||||
|
var_name: impl Into<String>,
|
||||||
|
value: Literal,
|
||||||
|
) -> Result<VariableLocation, Error> {
|
||||||
|
let var_name = var_name.into();
|
||||||
|
if self.constant_vars.contains_key(&var_name) {
|
||||||
|
return Err(Error::DuplicateVariable(var_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.constant_vars.insert(var_name, value.clone());
|
||||||
|
Ok(VariableLocation::Constant(value))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_location_of(&self, var_name: impl Into<String>) -> Result<VariableLocation, Error> {
|
pub fn get_location_of(&self, var_name: impl Into<String>) -> Result<VariableLocation, Error> {
|
||||||
let var_name = var_name.into();
|
let var_name = var_name.into();
|
||||||
|
|
||||||
@@ -190,7 +210,7 @@ impl<'a> VariableScope<'a> {
|
|||||||
"Attempted to free a `let` variable.",
|
"Attempted to free a `let` variable.",
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
VariableLocation::Stack(_) => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user