Skip to content
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
2 changes: 1 addition & 1 deletion crates/beamtalk-cli/src/commands/test_stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ pub(crate) fn compile_expression_to_core(
.ok_or_else(|| "No expression found in parsed source".to_string())?;

// Generate Core Erlang test module (no workspace bindings)
beamtalk_core::codegen::core_erlang::generate_test_expression(expr, module_name)
beamtalk_core::codegen::core_erlang::generate_test_expression(&expr.expression, module_name)
.map_err(|e| format!("{e}"))
}

Expand Down
17 changes: 12 additions & 5 deletions crates/beamtalk-compiler-port/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,12 @@ fn handle_compile_expression(request: &Map) -> Term {
}

// BT-780: Generate Core Erlang for all expressions (multi-statement support)
match beamtalk_core::erlang::generate_repl_expressions(&module.expressions, &module_name) {
let expressions: Vec<_> = module
.expressions
.iter()
.map(|s| s.expression.clone())
.collect();
match beamtalk_core::erlang::generate_repl_expressions(&expressions, &module_name) {
Ok(code) => ok_response(&code, &warnings),
Err(e) => error_response(&[format!("Code generation failed: {e}")]),
}
Expand Down Expand Up @@ -485,10 +490,12 @@ fn handle_inline_class_definition(
let trailing_core_erlang = if module.expressions.is_empty() {
None
} else {
match beamtalk_core::erlang::generate_repl_expressions(
&module.expressions,
expr_module_name,
) {
let trailing_exprs: Vec<_> = module
.expressions
.iter()
.map(|s| s.expression.clone())
.collect();
match beamtalk_core::erlang::generate_repl_expressions(&trailing_exprs, expr_module_name) {
Ok(code) => Some(code),
Err(e) => {
return error_response(&[format!(
Expand Down
66 changes: 35 additions & 31 deletions crates/beamtalk-core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! classes: Vec::new(),
//! method_definitions: Vec::new(),
//! expressions: vec![
//! Expression::Assignment {
//! ExpressionStatement::bare(Expression::Assignment {
//! target: Box::new(Expression::Identifier(Identifier {
//! name: "x".into(),
//! span: Span::new(0, 1),
Expand All @@ -53,10 +53,10 @@
//! span: Span::new(5, 10),
//! }),
//! span: Span::new(0, 10),
//! }
//! })
//! ],
//! span: Span::new(0, 10),
//! leading_comments: Vec::new(),
//! file_leading_comments: Vec::new(),
//! };
//! # assert_eq!(module.expressions.len(), 1);
//! ```
Expand Down Expand Up @@ -96,23 +96,25 @@ pub struct Module {
/// Standalone method definitions (Tonel-style `Class >> method => body`).
pub method_definitions: Vec<StandaloneMethodDefinition>,
/// Top-level expressions (scripts, REPL input).
pub expressions: Vec<Expression>,
pub expressions: Vec<ExpressionStatement>,
/// Source location spanning the entire module.
pub span: Span,
/// Leading comments (module documentation).
pub leading_comments: Vec<Comment>,
/// File-level leading comments (ADR 0044).
///
/// Comments at the very start of the file, before any expressions or class definitions.
pub file_leading_comments: Vec<Comment>,
}

impl Module {
/// Creates a new module with the given expressions and span.
#[must_use]
pub fn new(expressions: Vec<Expression>, span: Span) -> Self {
pub fn new(expressions: Vec<ExpressionStatement>, span: Span) -> Self {
Self {
classes: Vec::new(),
method_definitions: Vec::new(),
expressions,
span,
leading_comments: Vec::new(),
file_leading_comments: Vec::new(),
}
}

Expand All @@ -124,23 +126,23 @@ impl Module {
method_definitions: Vec::new(),
expressions: Vec::new(),
span,
leading_comments: Vec::new(),
file_leading_comments: Vec::new(),
}
}

/// Creates a new module with comments.
/// Creates a new module with file-level leading comments.
#[must_use]
pub fn with_comments(
expressions: Vec<Expression>,
expressions: Vec<ExpressionStatement>,
span: Span,
leading_comments: Vec<Comment>,
file_leading_comments: Vec<Comment>,
) -> Self {
Self {
classes: Vec::new(),
method_definitions: Vec::new(),
expressions,
span,
leading_comments,
file_leading_comments,
}
}
}
Expand Down Expand Up @@ -215,8 +217,7 @@ impl CommentAttachment {
///
/// Wraps an [`Expression`] with a [`CommentAttachment`] for preserving comments
/// between statements. Statement-position fields (method bodies, block bodies,
/// module expressions) will migrate from `Vec<Expression>` to
/// `Vec<ExpressionStatement>` in BT-974.
/// module expressions) use `Vec<ExpressionStatement>` (BT-974).
#[derive(Debug, Clone, PartialEq)]
pub struct ExpressionStatement {
/// Comments attached to this statement.
Expand Down Expand Up @@ -547,7 +548,7 @@ pub struct MethodDefinition {
/// Method parameters with optional type annotations.
pub parameters: Vec<ParameterDefinition>,
/// The method body expressions.
pub body: Vec<Expression>,
pub body: Vec<ExpressionStatement>,
/// Optional return type annotation.
pub return_type: Option<TypeAnnotation>,
/// Whether this method is sealed (cannot be overridden).
Expand All @@ -568,7 +569,7 @@ impl MethodDefinition {
pub fn new(
selector: MessageSelector,
parameters: Vec<ParameterDefinition>,
body: Vec<Expression>,
body: Vec<ExpressionStatement>,
span: Span,
) -> Self {
Self {
Expand All @@ -589,7 +590,7 @@ impl MethodDefinition {
pub fn with_return_type(
selector: MessageSelector,
parameters: Vec<ParameterDefinition>,
body: Vec<Expression>,
body: Vec<ExpressionStatement>,
return_type: TypeAnnotation,
span: Span,
) -> Self {
Expand All @@ -611,7 +612,7 @@ impl MethodDefinition {
pub fn with_options(
selector: MessageSelector,
parameters: Vec<ParameterDefinition>,
body: Vec<Expression>,
body: Vec<ExpressionStatement>,
return_type: Option<TypeAnnotation>,
is_sealed: bool,
kind: MethodKind,
Expand Down Expand Up @@ -1251,15 +1252,19 @@ pub struct Block {
/// Block parameters.
pub parameters: Vec<BlockParameter>,
/// The expressions in the block body.
pub body: Vec<Expression>,
pub body: Vec<ExpressionStatement>,
/// Source location of the entire block (including brackets).
pub span: Span,
}

impl Block {
/// Creates a new block.
#[must_use]
pub fn new(parameters: Vec<BlockParameter>, body: Vec<Expression>, span: Span) -> Self {
pub fn new(
parameters: Vec<BlockParameter>,
body: Vec<ExpressionStatement>,
span: Span,
) -> Self {
Self {
parameters,
body,
Expand Down Expand Up @@ -1541,7 +1546,7 @@ mod tests {
let module = Module::new(Vec::new(), span);
assert!(module.expressions.is_empty());
assert_eq!(module.span, span);
assert!(module.leading_comments.is_empty());
assert!(module.file_leading_comments.is_empty());
}

#[test]
Expand Down Expand Up @@ -1896,7 +1901,7 @@ mod tests {
#[test]
fn method_definition_unary() {
let selector = MessageSelector::Unary("increment".into());
let body = vec![Expression::Assignment {
let body = vec![ExpressionStatement::bare(Expression::Assignment {
target: Box::new(Expression::FieldAccess {
receiver: Box::new(Expression::Identifier(Identifier::new(
"self",
Expand All @@ -1920,7 +1925,7 @@ mod tests {
span: Span::new(14, 28),
}),
span: Span::new(0, 28),
}];
})];
let method = MethodDefinition::new(selector, Vec::new(), body, Span::new(0, 28));
assert_eq!(method.selector.name(), "increment");
assert!(method.parameters.is_empty());
Expand All @@ -1935,9 +1940,8 @@ mod tests {
"index",
Span::new(4, 9),
))];
let body = vec![Expression::Identifier(Identifier::new(
"result",
Span::new(13, 19),
let body = vec![ExpressionStatement::bare(Expression::Identifier(
Identifier::new("result", Span::new(13, 19)),
))];
let method = MethodDefinition::new(selector, params, body, Span::new(0, 19));
assert_eq!(method.selector.name(), "at:");
Expand All @@ -1948,7 +1952,7 @@ mod tests {
#[test]
fn method_definition_with_return_type() {
let selector = MessageSelector::Unary("getValue".into());
let body = vec![Expression::Return {
let body = vec![ExpressionStatement::bare(Expression::Return {
value: Box::new(Expression::FieldAccess {
receiver: Box::new(Expression::Identifier(Identifier::new(
"self",
Expand All @@ -1958,7 +1962,7 @@ mod tests {
span: Span::new(0, 10),
}),
span: Span::new(0, 10),
}];
})];
let return_type = TypeAnnotation::simple("Integer", Span::new(12, 19));
let method = MethodDefinition::with_return_type(
selector,
Expand All @@ -1984,7 +1988,7 @@ mod tests {
let methods = vec![MethodDefinition::new(
MessageSelector::Unary("increment".into()),
Vec::new(),
vec![Expression::Assignment {
vec![ExpressionStatement::bare(Expression::Assignment {
target: Box::new(Expression::FieldAccess {
receiver: Box::new(Expression::Identifier(Identifier::new(
"self",
Expand All @@ -2008,7 +2012,7 @@ mod tests {
span: Span::new(64, 78),
}),
span: Span::new(50, 78),
}],
})],
Span::new(45, 78),
)];

Expand Down
12 changes: 6 additions & 6 deletions crates/beamtalk-core/src/ast_walker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//! awareness (e.g. `cascade_candidate`) keep their own recursive traversal.
//! This module handles the common pre-order-visitor pattern.

use crate::ast::{Expression, Module, StringSegment};
use crate::ast::{Expression, ExpressionStatement, Module, StringSegment};

// ── Module-level iterators ────────────────────────────────────────────────────

Expand All @@ -44,7 +44,7 @@ use crate::ast::{Expression, Module, StringSegment};
/// not top-level statement sequences.
pub(crate) fn for_each_expr_seq<F>(module: &Module, mut f: F)
where
F: FnMut(&[Expression]),
F: FnMut(&[ExpressionStatement]),
{
f(&module.expressions);
for class in &module.classes {
Expand Down Expand Up @@ -81,8 +81,8 @@ where
}
}
Expression::Block(block) => {
for e in &block.body {
walk_expression(e, f);
for stmt in &block.body {
walk_expression(&stmt.expression, f);
}
}
Expression::Assignment { target, value, .. } => {
Expand Down Expand Up @@ -165,8 +165,8 @@ where
F: FnMut(&Expression),
{
for_each_expr_seq(module, |seq| {
for expr in seq {
walk_expression(expr, f);
for stmt in seq {
walk_expression(&stmt.expression, f);
}
});
}
4 changes: 2 additions & 2 deletions crates/beamtalk-core/src/codegen/core_erlang/actor_codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl CoreErlangGenerator {
c.methods.iter().any(|m| {
m.body.len() == 1
&& matches!(
&m.body[0],
&m.body[0].expression,
Expression::Primitive {
is_quoted: true,
..
Expand Down Expand Up @@ -382,7 +382,7 @@ impl CoreErlangGenerator {
let needs_nlr = method
.body
.iter()
.any(|expr| Self::expr_has_block_nlr(expr, false));
.any(|stmt| Self::expr_has_block_nlr(&stmt.expression, false));

let nlr_token_var = if needs_nlr {
let token_var = self.fresh_temp_var("NlrToken");
Expand Down
Loading