Skip to content

Commit

Permalink
add echo typed node
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocavalieri committed Oct 10, 2024
1 parent b83e497 commit 6567af2
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 3 deletions.
23 changes: 23 additions & 0 deletions compiler-core/src/ast/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ pub enum TypedExpr {
type_: Arc<Type>,
},

Echo {
location: SrcSpan,
type_: Arc<Type>,
expression: Option<Box<Self>>,
},

BitArray {
location: SrcSpan,
type_: Arc<Type>,
Expand Down Expand Up @@ -182,6 +188,11 @@ impl TypedExpr {
| Self::ModuleSelect { .. }
| Self::Invalid { .. } => self.self_if_contains_location(byte_index),

Self::Echo { expression, .. } => expression
.as_ref()
.and_then(|e| e.find_node(byte_index))
.or_else(|| self.self_if_contains_location(byte_index)),

Self::Todo { kind, .. } => match kind {
TodoKind::Keyword => self.self_if_contains_location(byte_index),
// We don't want to match on todos that were implicitly inserted
Expand Down Expand Up @@ -316,6 +327,7 @@ impl TypedExpr {
| Self::Int { location, .. }
| Self::Var { location, .. }
| Self::Todo { location, .. }
| Self::Echo { location, .. }
| Self::Case { location, .. }
| Self::Call { location, .. }
| Self::List { location, .. }
Expand Down Expand Up @@ -343,6 +355,7 @@ impl TypedExpr {
| Self::Int { location, .. }
| Self::Var { location, .. }
| Self::Todo { location, .. }
| Self::Echo { location, .. }
| Self::Case { location, .. }
| Self::Call { location, .. }
| Self::List { location, .. }
Expand Down Expand Up @@ -372,6 +385,7 @@ impl TypedExpr {
| TypedExpr::Call { .. }
| TypedExpr::Case { .. }
| TypedExpr::Todo { .. }
| TypedExpr::Echo { .. }
| TypedExpr::Panic { .. }
| TypedExpr::BinOp { .. }
| TypedExpr::Float { .. }
Expand Down Expand Up @@ -413,6 +427,7 @@ impl TypedExpr {
Self::Fn { type_, .. }
| Self::Int { type_, .. }
| Self::Todo { type_, .. }
| Self::Echo { type_, .. }
| Self::Case { type_, .. }
| Self::List { type_, .. }
| Self::Call { type_, .. }
Expand Down Expand Up @@ -473,6 +488,7 @@ impl TypedExpr {
| TypedExpr::Tuple { .. }
| TypedExpr::TupleIndex { .. }
| TypedExpr::Todo { .. }
| TypedExpr::Echo { .. }
| TypedExpr::Panic { .. }
| TypedExpr::BitArray { .. }
| TypedExpr::RecordUpdate { .. }
Expand Down Expand Up @@ -549,6 +565,13 @@ impl TypedExpr {
// pure value constructor and raise a warning for those as well.
TypedExpr::Block { .. } | TypedExpr::Case { .. } => false,

// `echo` just returns the thing its printing, so it's pure as long as
// the printed thing is. If it's being used as a step of a pipeline then
// we always consider it pure
TypedExpr::Echo { expression, .. } => expression
.as_ref()
.map_or(true, |e| e.is_pure_value_constructor()),

// `panic`, `todo`, and placeholders are never considered pure value constructors,
// we don't want to raise a warning for an unused value if it's one
// of those.
Expand Down
27 changes: 27 additions & 0 deletions compiler-core/src/ast/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ pub trait Visit<'ast> {
visit_typed_expr(self, expr);
}

fn visit_typed_expr_echo(
&mut self,
location: &'ast SrcSpan,
type_: &'ast Arc<Type>,
expression: &'ast Option<Box<TypedExpr>>,
) {
visit_typed_expr_echo(self, location, type_, expression);
}

fn visit_typed_expr_int(
&mut self,
location: &'ast SrcSpan,
Expand Down Expand Up @@ -612,6 +621,11 @@ where
}
TypedExpr::NegateInt { location, value } => v.visit_typed_expr_negate_int(location, value),
TypedExpr::Invalid { location, type_ } => v.visit_typed_expr_invalid(location, type_),
TypedExpr::Echo {
location,
expression,
type_,
} => v.visit_typed_expr_echo(location, type_, expression),
}
}

Expand Down Expand Up @@ -830,6 +844,19 @@ pub fn visit_typed_expr_todo<'a, V>(
}
}

fn visit_typed_expr_echo<'a, V>(
v: &mut V,
_location: &'a SrcSpan,
_type_: &'a Arc<Type>,
expression: &'a Option<Box<TypedExpr>>,
) where
V: Visit<'a> + ?Sized,
{
if let Some(expression) = expression {
v.visit_typed_expr(expression)
}
}

pub fn visit_typed_expr_panic<'a, V>(
v: &mut V,
_location: &'a SrcSpan,
Expand Down
7 changes: 7 additions & 0 deletions compiler-core/src/erlang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,7 @@ fn needs_begin_end_wrapping(expression: &TypedExpr) -> bool {
| TypedExpr::Tuple { .. }
| TypedExpr::TupleIndex { .. }
| TypedExpr::Todo { .. }
| TypedExpr::Echo { .. }
| TypedExpr::Panic { .. }
| TypedExpr::BitArray { .. }
| TypedExpr::RecordUpdate { .. }
Expand Down Expand Up @@ -1867,6 +1868,12 @@ fn expr<'a>(expression: &'a TypedExpr, env: &mut Env<'a>) -> Document<'a> {
),

TypedExpr::Invalid { .. } => panic!("invalid expressions should not reach code generation"),

TypedExpr::Echo {
location: _,
expression: _,
type_: _,
} => todo!("generate code for echo"),
}
}

Expand Down
10 changes: 10 additions & 0 deletions compiler-core/src/javascript/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ impl<'module> Generator<'module> {

TypedExpr::NegateInt { value, .. } => self.negate_with("- ", value),

TypedExpr::Echo {
location: _,
expression: _,
type_: _,
} => todo!("generate js code for echo"),

TypedExpr::Invalid { .. } => {
panic!("invalid expressions should not reach code generation")
}
Expand Down Expand Up @@ -1636,6 +1642,8 @@ impl TypedExpr {
| TypedExpr::Block { .. }
| TypedExpr::Pipeline { .. } => true,

TypedExpr::Echo { .. } => todo!("understand what this means"),

TypedExpr::Int { .. }
| TypedExpr::Float { .. }
| TypedExpr::String { .. }
Expand Down Expand Up @@ -1711,6 +1719,8 @@ fn requires_semicolon(statement: &TypedStatement) -> bool {
| TypedExpr::Block { .. },
) => true,

Statement::Expression(TypedExpr::Echo { .. }) => todo!("understand if it is required"),

Statement::Expression(
TypedExpr::Todo { .. }
| TypedExpr::Case { .. }
Expand Down
8 changes: 6 additions & 2 deletions compiler-core/src/type_/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
expression: Option<Box<UntypedExpr>>,
) -> Result<TypedExpr, Error> {
if let Some(expression) = expression {
let typed_expression = self.infer(*expression);
Ok(todo!("typed expr ECHO"))
let expression = self.infer(*expression)?;
Ok(TypedExpr::Echo {
location,
type_: expression.type_(),
expression: Some(Box::new(expression)),
})
} else {
Err(Error::EchoWithNoFollowingExpression { location })
}
Expand Down
11 changes: 10 additions & 1 deletion compiler-core/src/type_/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,16 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
location,
expression: None,
} => {
todo!("This echo is actually ok!")
// An echo that is not followed by an expression that is
// used as a pipeline's step is just like the identity
// function.
// So it gets the type of the value coming from the previous
// step of the pipeline.
TypedExpr::Echo {
location,
expression: None,
type_: self.argument_type.clone(),
}
}

// right(left)
Expand Down
1 change: 1 addition & 0 deletions compiler-core/src/type_/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod assert;
mod assignments;
mod conditional_compilation;
mod custom_types;
mod echo;
mod errors;
mod exhaustiveness;
mod externals;
Expand Down
99 changes: 99 additions & 0 deletions compiler-core/src/type_/tests/echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::{assert_module_error, assert_module_infer};

#[test]
pub fn echo_has_same_type_as_printed_expression() {
assert_module_infer!(
r#"
pub fn main() {
echo 1
}
"#,
vec![("main", "fn() -> Int")]
);
}

#[test]
pub fn echo_has_same_type_as_printed_expression_2() {
assert_module_infer!(
r#"
pub fn main() {
let wibble = todo
echo wibble
}
"#,
vec![("main", "fn() -> a")]
);
}

#[test]
pub fn echo_in_pipeline_acts_as_the_identity_function() {
assert_module_infer!(
r#"
pub fn main() {
[1, 2, 3]
|> echo
}
"#,
vec![("main", "fn() -> List(Int)")]
);
}

#[test]
pub fn echo_in_pipeline_acts_as_the_identity_function_2() {
assert_module_infer!(
r#"
pub fn main() {
1
|> echo
|> fn(_: Int) { True }
}
"#,
vec![("main", "fn() -> Bool")]
);
}

#[test]
pub fn echo_in_pipeline_acts_as_the_identity_function_3() {
assert_module_infer!(
r#"
pub fn main() {
[1, 2, 3]
|> echo
|> echo
|> wibble
}
fn wibble(_: List(Int)) -> List(String) { todo }
"#,
vec![("main", "fn() -> List(String)")]
);
}

#[test]
pub fn echo_in_pipeline_printing_a_function() {
assert_module_infer!(
r#"
pub fn main() {
[1, 2, 3]
|> echo wibble
}
fn wibble(_: List(Int)) -> List(String) { todo }
"#,
vec![("main", "fn() -> List(String)")]
);
}

#[test]
pub fn echo_in_pipeline_printing_a_function_2() {
assert_module_error!(
r#"
pub fn main() {
[1, 2, 3]
|> echo wibble
}
fn wibble(_: List(Float)) -> List(String) { todo }
"#
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
source: compiler-core/src/type_/tests/echo.rs
expression: "\npub fn main() {\n [1, 2, 3]\n |> echo wibble\n}\n\nfn wibble(_: List(Float)) -> List(String) { todo }\n"
---
error: Type mismatch
┌─ /src/one/two.gleam:4:6
4|> echo wibble
^^^^^^^^^^^ This function does not accept the piped type

The argument is:

List(Int)

But function expects:

List(Float)

0 comments on commit 6567af2

Please sign in to comment.