Skip to content

Commit

Permalink
Merge pull request #52 from Ph0enixKM/A74
Browse files Browse the repository at this point in the history
A74 Add Type Casting
  • Loading branch information
Ph0enixKM authored Dec 23, 2022
2 parents f5059cc + e925d5c commit 2fcaa46
Show file tree
Hide file tree
Showing 20 changed files with 172 additions and 55 deletions.
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

0 comments on commit 2fcaa46

Please sign in to comment.