Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A74 Add Type Casting #52

Merged
merged 2 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
heraclitus-compiler = "1.5.6"
heraclitus-compiler = "1.5.8"
similar-string = "1.4.2"
colored = "2.0.0"
itertools = "0.10.5"
Expand Down
9 changes: 8 additions & 1 deletion src/modules/block.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::VecDeque;

use heraclitus_compiler::prelude::*;
use crate::{utils::{metadata::ParserMetadata, TranslateMetadata}};
use crate::translate::module::TranslateModule;
Expand Down Expand Up @@ -38,7 +40,7 @@ impl SyntaxModule<ParserMetadata> for Block {
continue;
}
// Handle comments
if token.word.starts_with('#') {
if token.word.starts_with("//") {
meta.increment_index();
continue
}
Expand All @@ -62,6 +64,9 @@ impl SyntaxModule<ParserMetadata> for Block {

impl TranslateModule for Block {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
// Save the current statement queue and create a new one
let mut new_queue = VecDeque::new();
std::mem::swap(&mut meta.stmt_queue, &mut new_queue);
meta.increase_indent();
let result = if self.is_empty() {
":".to_string()
Expand All @@ -73,6 +78,8 @@ impl TranslateModule for Block {
.collect::<Vec<_>>().join(";\n")
};
meta.decrease_indent();
// Restore the old statement queue
std::mem::swap(&mut meta.stmt_queue, &mut new_queue);
result
}
}
2 changes: 1 addition & 1 deletion src/modules/condition/ifchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl SyntaxModule<ParserMetadata> for IfChain {
let mut cond = Expr::new();
let mut block = Block::new();
// Handle comments and empty lines
if token_by(meta, |token| token.starts_with('#') || token.starts_with('\n')).is_ok() {
if token_by(meta, |token| token.starts_with("//") || token.starts_with('\n')).is_ok() {
continue
}
// Handle else keyword
Expand Down
7 changes: 5 additions & 2 deletions src/modules/condition/ifcond.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use heraclitus_compiler::prelude::*;
use crate::modules::expression::expr::Expr;
use crate::translate::module::TranslateModule;
use crate::utils::cc_flags::{CCFlags, get_ccflag_name};
use crate::utils::metadata::{ParserMetadata, TranslateMetadata};
use crate::modules::block::Block;
use crate::modules::statement::stmt::{Statement, StatementType};
Expand All @@ -15,10 +16,12 @@ pub struct IfCondition {
impl IfCondition {
fn prevent_not_using_if_chain(&self, meta: &mut ParserMetadata, statement: &Statement, tok: Option<Token>) -> Result<(), Failure> {
let is_not_if_chain = matches!(statement.value.as_ref().unwrap(), StatementType::IfCondition(_) | StatementType::IfChain(_));
if is_not_if_chain {
if is_not_if_chain && !meta.context.cc_flags.contains(&CCFlags::AllowNestedIfElse) {
let flag_name = get_ccflag_name(CCFlags::AllowNestedIfElse);
// TODO: [A34] Add a comment pointing to the website documentation
let message = Message::new_warn_at_token(meta, tok)
.message("You should use if-chain instead of nested if else statements");
.message("You should use if-chain instead of nested if else statements")
.comment(format!("To surpress this warning, use #[{flag_name}] before the parent function declaration"));
meta.add_message(message);
}
Ok(())
Expand Down
10 changes: 7 additions & 3 deletions src/modules/expression/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use super::binop::{
neq::Neq
};
use super::unop::{
not::Not
not::Not,
cast::Cast
};
use super::parenthesis::Parenthesis;
use crate::modules::variable::get::VariableGet;
Expand Down Expand Up @@ -61,7 +62,8 @@ pub enum ExprType {
FunctionInvocation(FunctionInvocation),
Array(Array),
Range(Range),
Null(Null)
Null(Null),
Cast(Cast)
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -97,11 +99,13 @@ impl Expr {
// Ternary conditional
Ternary,
// Logical operators
And, Or, Not,
And, Or,
// Comparison operators
Gt, Ge, Lt, Le, Eq, Neq,
// Arithmetic operators
Add, Sub, Mul, Div, Modulo,
// Unary operators
Cast, Not,
// Literals
Range, Parenthesis, CommandExpr, Bool, Number, Text, Array, Null,
// Function invocation
Expand Down
53 changes: 53 additions & 0 deletions src/modules/expression/unop/cast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use heraclitus_compiler::prelude::*;
use crate::{utils::{metadata::ParserMetadata, TranslateMetadata, cc_flags::{get_ccflag_name, CCFlags}}, modules::{types::{Type, Typed, parse_type}, expression::binop::parse_left_expr}, translate::{module::TranslateModule}};
use super::super::expr::Expr;

#[derive(Debug, Clone)]
pub struct Cast {
expr: Box<Expr>,
kind: Type
}

impl Typed for Cast {
fn get_type(&self) -> Type {
self.kind.clone()
}
}

impl SyntaxModule<ParserMetadata> for Cast {
syntax_name!("Cast");

fn new() -> Self {
Cast {
expr: Box::new(Expr::new()),
kind: Type::Generic
}
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
parse_left_expr(meta, &mut *self.expr, "as")?;
let tok = meta.get_current_token();
token(meta, "as")?;
self.kind = parse_type(meta)?;
if !meta.context.cc_flags.contains(&CCFlags::AllowAbsurdCast) {
let flag_name = get_ccflag_name(CCFlags::AllowAbsurdCast);
let l_type = self.expr.get_type();
let r_type = self.kind.clone();
let message = Message::new_warn_at_token(meta, tok)
.message(format!("Casting a value of type '{l_type}' value to a '{r_type}' is not recommended"))
.comment(format!("To suppress this warning, use #[{flag_name}] before the parent function declaration"));
match self.kind {
Type::Array(_) | Type::Null => meta.add_message(message),
Type::Num => if self.expr.get_type() == Type::Text { meta.add_message(message) },
_ => {}
}
}
Ok(())
}
}

impl TranslateModule for Cast {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
self.expr.translate(meta)
}
}
3 changes: 2 additions & 1 deletion src/modules/expression/unop/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod not;
pub mod not;
pub mod cast;
8 changes: 3 additions & 5 deletions src/modules/expression/unop/not.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ use super::super::expr::Expr;

#[derive(Debug, Clone)]
pub struct Not {
expr: Box<Expr>,
kind: Type
expr: Box<Expr>
}

impl Typed for Not {
fn get_type(&self) -> Type {
self.kind.clone()
Type::Bool
}
}

Expand All @@ -19,8 +18,7 @@ impl SyntaxModule<ParserMetadata> for Not {

fn new() -> Self {
Not {
expr: Box::new(Expr::new()),
kind: Type::Bool
expr: Box::new(Expr::new())
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/modules/function/declaration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::collections::HashSet;
use std::mem::swap;

use heraclitus_compiler::prelude::*;
use itertools::izip;
use crate::modules::types::Type;
use crate::modules::variable::variable_name_extensions;
use crate::utils::cc_flags::get_ccflag_by_name;
use crate::utils::function_cache::FunctionInstance;
use crate::utils::function_interface::FunctionInterface;
use crate::utils::metadata::{ParserMetadata, TranslateMetadata};
Expand Down Expand Up @@ -62,6 +66,12 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
let mut flags = HashSet::new();
// Get all the user-defined compiler flags
while let Ok(flag) = token_by(meta, |val| val.starts_with("#[")) {
// Push to the flags vector as it is more safe in case of parsing errors
flags.insert(get_ccflag_by_name(&flag[2..flag.len() - 1]));
}
// Check if this function is public
if token(meta, "pub").is_ok() {
self.is_public = true;
Expand All @@ -72,6 +82,8 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
self.name = variable(meta, variable_name_extensions())?;
handle_existing_function(meta, tok.clone())?;
context!({
// Set the compiler flags
swap(&mut meta.context.cc_flags, &mut flags);
// Get the arguments
token(meta, "(")?;
loop {
Expand Down Expand Up @@ -120,6 +132,8 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
returns: self.returns.clone(),
is_public: self.is_public
}, ctx)?;
// Restore the compiler flags
swap(&mut meta.context.cc_flags, &mut flags);
Ok(())
}, |pos| {
error_pos!(meta, pos, format!("Failed to parse function declaration '{}'", self.name))
Expand Down
6 changes: 4 additions & 2 deletions src/modules/function/declaration_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use heraclitus_compiler::prelude::*;
use crate::modules::types::Type;
use crate::utils::ParserMetadata;
use crate::modules::variable::{handle_identifier_name};
use crate::utils::cc_flags::{CCFlags, get_ccflag_name};
use crate::utils::context::Context;
use crate::utils::function_interface::FunctionInterface;

Expand Down Expand Up @@ -42,10 +43,11 @@ pub fn handle_add_function(meta: &mut ParserMetadata, tok: Option<Token>, fun: F
comment: "Please decide whether to use generics or types for all arguments"
})
}
if any_typed && fun.returns == Type::Generic {
if any_typed && fun.returns == Type::Generic && !meta.context.cc_flags.contains(&CCFlags::AllowGenericReturn) {
let flag_name = get_ccflag_name(CCFlags::AllowGenericReturn);
let message = Message::new_warn_at_token(meta, tok.clone())
.message("Function has typed arguments but a generic return type")
.comment(format!("To surpress this warning, specify a return type for the function '{name}'"));
.comment(format!("To surpress this warning, specify a return type for the function '{name}' or use #[{flag_name}] before the parent function declaration"));
meta.add_message(message);
}
// Try to add the function to the memory
Expand Down
30 changes: 15 additions & 15 deletions src/modules/function/ret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,41 @@ use crate::utils::metadata::{ParserMetadata, TranslateMetadata};
use crate::translate::module::TranslateModule;

#[derive(Debug, Clone)]
pub struct Ret {
pub struct Return {
pub expr: Expr
}

impl Typed for Ret {
impl Typed for Return {
fn get_type(&self) -> Type {
self.expr.get_type()
}
}

impl SyntaxModule<ParserMetadata> for Ret {
syntax_name!("Ret");
impl SyntaxModule<ParserMetadata> for Return {
syntax_name!("Return");

fn new() -> Self {
Ret {
Return {
expr: Expr::new()
}
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
let tok = meta.get_current_token();
token(meta, "ret")?;
token(meta, "return")?;
if !meta.context.is_fun_ctx {
return error!(meta, tok,
"Return statement outside of function",
"Return statements can only be used inside of functions"
);
return error!(meta, tok => {
message: "Return statement outside of function",
comment: "Return statements can only be used inside of functions"
});
}
syntax(meta, &mut self.expr)?;
match meta.context.fun_ret_type.as_ref() {
Some(ret_type) => if ret_type != &self.expr.get_type() {
return error!(meta, tok,
"Return type does not match function return type",
format!("Given type: {}, expected type: {}", self.expr.get_type(), ret_type)
);
return error!(meta, tok => {
message: "Return type does not match function return type",
comment: format!("Given type: {}, expected type: {}", self.expr.get_type(), ret_type)
});
},
None => {
meta.context.fun_ret_type = Some(self.expr.get_type());
Expand All @@ -49,7 +49,7 @@ impl SyntaxModule<ParserMetadata> for Ret {
}
}

impl TranslateModule for Ret {
impl TranslateModule for Return {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
let (name, id, variant) = meta.fun_name.clone().expect("Function name not set");
let result = self.expr.translate_eval(meta, false);
Expand Down
14 changes: 7 additions & 7 deletions src/modules/statement/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::modules::loops::{
};
use crate::modules::function::{
declaration::FunctionDeclaration,
ret::Ret
ret::Return
};
use crate::modules::imports::{
import::Import
Expand All @@ -54,7 +54,7 @@ pub enum StatementType {
Break(Break),
Continue(Continue),
FunctionDeclaration(FunctionDeclaration),
Ret(Ret),
Return(Return),
Import(Import),
Main(Main),
Echo(Echo)
Expand All @@ -70,7 +70,7 @@ impl Statement {
// Imports
Import,
// Functions
FunctionDeclaration, Main, Ret,
FunctionDeclaration, Main, Return,
// Loops
InfiniteLoop, IterLoop, Break, Continue,
// Conditions
Expand Down Expand Up @@ -118,7 +118,7 @@ impl SyntaxModule<ParserMetadata> for Statement {
for statement in statements {
// Handle comments
if let Some(token) = meta.get_current_token() {
if token.word.starts_with('#') {
if token.word.starts_with("//") {
meta.increment_index();
continue
}
Expand All @@ -143,9 +143,9 @@ impl TranslateModule for Statement {
// Translate the statement
let translated = self.translate_match(meta, self.value.as_ref().unwrap());
// This is a workaround that handles $(...) which cannot be used as a statement
let translated = if translated.starts_with('$') || translated.starts_with("\"$") {
format!("echo {} > /dev/null 2>&1", translated)
} else { translated };
let translated = (matches!(self.value, Some(StatementType::Expr(_))) || translated.starts_with('$') || translated.starts_with("\"$"))
.then(|| format!("echo {} > /dev/null 2>&1", translated))
.unwrap_or_else(|| translated);
// Get all the required supplemental statements
let indentation = meta.gen_indent();
let statements = meta.stmt_queue.drain(..).map(|st| indentation.clone() + &st + ";\n").join("");
Expand Down
Loading