Implement AST for 'const' expressions. TODO -- add const expressions to compiler
This commit is contained in:
@@ -149,6 +149,7 @@ fn test_spilled_variable_update_in_branch() -> anyhow::Result<()> {
|
|||||||
sub r0 sp 1
|
sub r0 sp 1
|
||||||
put db r0 99 #h
|
put db r0 99 #h
|
||||||
L1:
|
L1:
|
||||||
|
sub sp sp 1
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ fn no_arguments() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn let_var_args() -> anyhow::Result<()> {
|
fn let_var_args() -> anyhow::Result<()> {
|
||||||
|
// !IMPORTANT this needs to be stabilized as it currently incorrectly calculates sp offset at
|
||||||
|
// both ends of the cleanup lifecycle
|
||||||
let compiled = compile! {
|
let compiled = compile! {
|
||||||
debug
|
debug
|
||||||
"
|
"
|
||||||
@@ -64,6 +66,7 @@ fn let_var_args() -> anyhow::Result<()> {
|
|||||||
get r8 db r0
|
get r8 db r0
|
||||||
sub sp sp 1
|
sub sp sp 1
|
||||||
move r9 r15 #i
|
move r9 r15 #i
|
||||||
|
sub sp sp 1
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -123,6 +126,7 @@ fn inline_literal_args() -> anyhow::Result<()> {
|
|||||||
get r8 db r0
|
get r8 db r0
|
||||||
sub sp sp 1
|
sub sp sp 1
|
||||||
move r9 r15 #returnedValue
|
move r9 r15 #returnedValue
|
||||||
|
sub sp sp 1
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -164,6 +168,7 @@ fn mixed_args() -> anyhow::Result<()> {
|
|||||||
get r8 db r0
|
get r8 db r0
|
||||||
sub sp sp 1
|
sub sp sp 1
|
||||||
move r9 r15 #returnValue
|
move r9 r15 #returnValue
|
||||||
|
sub sp sp 1
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ fn variable_declaration_numeric_literal_stack_spillover() -> anyhow::Result<()>
|
|||||||
push 7 #h
|
push 7 #h
|
||||||
push 8 #i
|
push 8 #i
|
||||||
push 9 #j
|
push 9 #j
|
||||||
|
sub sp sp 3
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -462,6 +462,15 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenType::Keyword(Keyword::Const) => {
|
||||||
|
let spanned_const = self.spanned(|p| p.const_declaration())?;
|
||||||
|
|
||||||
|
Some(Spanned {
|
||||||
|
span: spanned_const.span,
|
||||||
|
node: Expression::ConstDeclaration(spanned_const),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
TokenType::Keyword(Keyword::Fn) => {
|
TokenType::Keyword(Keyword::Fn) => {
|
||||||
let spanned_fn = self.spanned(|p| p.function())?;
|
let spanned_fn = self.spanned(|p| p.function())?;
|
||||||
Some(Spanned {
|
Some(Spanned {
|
||||||
@@ -1220,6 +1229,46 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(BlockExpression(expressions))
|
Ok(BlockExpression(expressions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn const_declaration(&mut self) -> Result<ConstDeclarationExpression, Error> {
|
||||||
|
// const
|
||||||
|
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
||||||
|
if !self_matches_current!(self, TokenType::Keyword(Keyword::Const)) {
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
|
self.current_span(),
|
||||||
|
current_token.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable_name
|
||||||
|
let ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
let ident_span = Self::token_to_span(ident_token);
|
||||||
|
let ident = match ident_token.token_type {
|
||||||
|
TokenType::Identifier(ref id) => id.clone(),
|
||||||
|
_ => return Err(Error::UnexpectedToken(ident_span, ident_token.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
// `=`
|
||||||
|
let assign_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone();
|
||||||
|
if !token_matches!(assign_token, TokenType::Symbol(Symbol::Assign)) {
|
||||||
|
return Err(Error::UnexpectedToken(
|
||||||
|
Self::token_to_span(&assign_token),
|
||||||
|
assign_token,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// literal value
|
||||||
|
self.assign_next()?;
|
||||||
|
let lit = self.spanned(|p| p.literal())?;
|
||||||
|
|
||||||
|
Ok(ConstDeclarationExpression {
|
||||||
|
name: Spanned {
|
||||||
|
span: ident_span,
|
||||||
|
node: ident,
|
||||||
|
},
|
||||||
|
value: lit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn declaration(&mut self) -> Result<Expression, Error> {
|
fn declaration(&mut self) -> Result<Expression, Error> {
|
||||||
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
|
||||||
if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) {
|
if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) {
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
use crate::sys_call;
|
|
||||||
use helpers::Documentation;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_token_tree_docs() -> anyhow::Result<()> {
|
|
||||||
let syscall = sys_call::System::Yield;
|
|
||||||
|
|
||||||
assert_eq!(syscall.docs(), "");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! parser {
|
macro_rules! parser {
|
||||||
($input:expr) => {
|
($input:expr) => {
|
||||||
Parser::new(Tokenizer::from($input.to_owned()))
|
Parser::new(Tokenizer::from($input))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mod blocks;
|
mod blocks;
|
||||||
mod docs;
|
|
||||||
use super::Parser;
|
use super::Parser;
|
||||||
use super::Tokenizer;
|
use super::Tokenizer;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -33,7 +32,7 @@ fn test_declarations() -> Result<()> {
|
|||||||
// The below line should fail
|
// The below line should fail
|
||||||
let y = 234
|
let y = 234
|
||||||
"#;
|
"#;
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
let tokenizer = Tokenizer::from(input);
|
||||||
let mut parser = Parser::new(tokenizer);
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
let expression = parser.parse()?.unwrap();
|
||||||
@@ -45,6 +44,36 @@ fn test_declarations() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_const_declaration() -> Result<()> {
|
||||||
|
let input = r#"
|
||||||
|
const item = 20c;
|
||||||
|
const decimal = 200.15;
|
||||||
|
const nameConst = "str_lit";
|
||||||
|
"#;
|
||||||
|
let tokenizer = Tokenizer::from(input);
|
||||||
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"(const item = 293.15)",
|
||||||
|
parser.parse()?.unwrap().to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"(const decimal = 200.15)",
|
||||||
|
parser.parse()?.unwrap().to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
r#"(const nameConst = "str_lit")"#,
|
||||||
|
parser.parse()?.unwrap().to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(None, parser.parse()?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function_expression() -> Result<()> {
|
fn test_function_expression() -> Result<()> {
|
||||||
let input = r#"
|
let input = r#"
|
||||||
@@ -54,7 +83,7 @@ fn test_function_expression() -> Result<()> {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
let tokenizer = Tokenizer::from(input);
|
||||||
let mut parser = Parser::new(tokenizer);
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
let expression = parser.parse()?.unwrap();
|
||||||
@@ -73,7 +102,7 @@ fn test_function_invocation() -> Result<()> {
|
|||||||
add();
|
add();
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
let tokenizer = Tokenizer::from(input);
|
||||||
let mut parser = Parser::new(tokenizer);
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
let expression = parser.parse()?.unwrap();
|
||||||
@@ -89,7 +118,7 @@ fn test_priority_expression() -> Result<()> {
|
|||||||
let x = (4);
|
let x = (4);
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
let tokenizer = Tokenizer::from(input);
|
||||||
let mut parser = Parser::new(tokenizer);
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|||||||
@@ -195,6 +195,18 @@ impl std::fmt::Display for LiteralOrVariable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct ConstDeclarationExpression {
|
||||||
|
pub name: Spanned<String>,
|
||||||
|
pub value: Spanned<Literal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ConstDeclarationExpression {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(const {} = {})", self.name, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct DeviceDeclarationExpression {
|
pub struct DeviceDeclarationExpression {
|
||||||
/// any variable-like name
|
/// any variable-like name
|
||||||
@@ -316,6 +328,7 @@ pub enum Expression {
|
|||||||
Binary(Spanned<BinaryExpression>),
|
Binary(Spanned<BinaryExpression>),
|
||||||
Block(Spanned<BlockExpression>),
|
Block(Spanned<BlockExpression>),
|
||||||
Break(Span),
|
Break(Span),
|
||||||
|
ConstDeclaration(Spanned<ConstDeclarationExpression>),
|
||||||
Continue(Span),
|
Continue(Span),
|
||||||
Declaration(Spanned<String>, Box<Spanned<Expression>>),
|
Declaration(Spanned<String>, Box<Spanned<Expression>>),
|
||||||
DeviceDeclaration(Spanned<DeviceDeclarationExpression>),
|
DeviceDeclaration(Spanned<DeviceDeclarationExpression>),
|
||||||
@@ -342,6 +355,7 @@ impl std::fmt::Display for Expression {
|
|||||||
Expression::Binary(e) => write!(f, "{}", e),
|
Expression::Binary(e) => write!(f, "{}", e),
|
||||||
Expression::Block(e) => write!(f, "{}", e),
|
Expression::Block(e) => write!(f, "{}", e),
|
||||||
Expression::Break(_) => write!(f, "break"),
|
Expression::Break(_) => write!(f, "break"),
|
||||||
|
Expression::ConstDeclaration(e) => write!(f, "{}", e),
|
||||||
Expression::Continue(_) => write!(f, "continue"),
|
Expression::Continue(_) => write!(f, "continue"),
|
||||||
Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e),
|
Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e),
|
||||||
Expression::DeviceDeclaration(e) => write!(f, "{}", e),
|
Expression::DeviceDeclaration(e) => write!(f, "{}", e),
|
||||||
@@ -362,4 +376,3 @@ impl std::fmt::Display for Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -463,6 +463,7 @@ impl<'a> Tokenizer<'a> {
|
|||||||
"break" if next_ws!() => keyword!(Break),
|
"break" if next_ws!() => keyword!(Break),
|
||||||
"while" if next_ws!() => keyword!(While),
|
"while" if next_ws!() => keyword!(While),
|
||||||
"continue" if next_ws!() => keyword!(Continue),
|
"continue" if next_ws!() => keyword!(Continue),
|
||||||
|
"const" if next_ws!() => keyword!(Const),
|
||||||
"true" if next_ws!() => {
|
"true" if next_ws!() => {
|
||||||
return Ok(Token::new(
|
return Ok(Token::new(
|
||||||
TokenType::Boolean(true),
|
TokenType::Boolean(true),
|
||||||
|
|||||||
@@ -295,6 +295,20 @@ documented! {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
Continue,
|
Continue,
|
||||||
|
/// Prepresents the `const` keyword. This allows you to define a variable that will never
|
||||||
|
/// change throughout the lifetime of the program, similar to `define` in IC10. If you are
|
||||||
|
/// not planning on mutating the variable (changing it), it is recommend you store it as a
|
||||||
|
/// const, as the compiler will not assign it to a register or stack variable.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// const targetTemp = 20c;
|
||||||
|
/// device gasSensor = "d0";
|
||||||
|
/// device airCon = "d1";
|
||||||
|
///
|
||||||
|
/// airCon.On = gasSensor.Temperature > targetTemp;
|
||||||
|
/// ```
|
||||||
|
Const,
|
||||||
/// Represents the `let` keyword, used to declare variables within Slang.
|
/// Represents the `let` keyword, used to declare variables within Slang.
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
@@ -304,6 +318,9 @@ documented! {
|
|||||||
/// ```
|
/// ```
|
||||||
Let,
|
Let,
|
||||||
/// Represents the `fn` keyword, used to declare functions within Slang.
|
/// Represents the `fn` keyword, used to declare functions within Slang.
|
||||||
|
/// # WARNING
|
||||||
|
/// Functions are currently unstable and are subject to change until stabilized. Use at
|
||||||
|
/// your own risk! (They are also heavily not optimized and produce a LOT of code bloat)
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
/// // This allows you to now call `doSomething` with specific arguments.
|
/// // This allows you to now call `doSomething` with specific arguments.
|
||||||
|
|||||||
Reference in New Issue
Block a user