///|
pub(all) struct Upper(String) derive(Hash, Eq)
///|
pub impl Show for Upper with output(self, logger) {
logger.write_string(self.inner())
}
///|
pub(all) struct Ident(String) derive(Hash, Eq)
///|
pub impl Show for Ident with output(self, logger) {
logger.write_string(self.inner())
}
///| Show for Type
///
/// ```moonbit
/// inspect(Type::Unit, content="Unit")
/// inspect(Type::Bool, content="Bool")
/// inspect(Type::Int, content="Int")
/// inspect(Type::Double, content="Double")
/// inspect(Type::Array(Int), content="Array[Int]")
/// inspect(Type::Tuple([Int, Bool]), content="(Int, Bool)")
/// inspect(Type::Func([Int, Bool], Int), content="(Int, Bool) -> Int")
/// inspect(Type::GenericDef("T"), content="T")
/// inspect(Type::Struct("Point", []), content="Point")
/// inspect(Type::GenericSub("Complex", Int), content="Complex[Int]")
/// ```
pub(all) enum Type {
Unit
Bool
Int
Double
Array(Type)
Tuple(Array[Type])
Func(Array[Type], Type) // (Int, Bool) -> Int
// User-defined type, e.g. Point
Struct(Upper, Array[(Ident, Type)])
Enum(Upper, Array[(Upper, Array[Type])]) // e.g. Color::Red(Int, Int, Int)
GenericDef(Upper) // T
GenericSub(Upper, Type) // Complex[Int]
} derive(Eq)
///|
pub impl Show for Type with output(self, logger) {
let s = match self {
Unit => "Unit"
Bool => "Bool"
Int => "Int"
Double => "Double"
Array(inner) => "Array[\{inner}]"
Tuple(inner) => {
let inner_str = inner.map(t => t.to_string()).join(", ")
"(\{inner_str})"
}
Func(args, ret) => {
let args_str = args.map(t => t.to_string()).join(", ")
"(\{args_str}) -> \{ret}"
}
Struct(name, _) => "\{name}"
Enum(name, _) => "\{name}"
GenericDef(name) => "\{name}"
GenericSub(name, inner) => "\{name}[\{inner}]"
}
logger.write_string(s)
}
///| ValueExpr is primary expressions that can be evaluated to a value.
///
/// ```mbt
/// inspect(ValueExpr::UnitExpr, content="()")
/// inspect(ValueExpr::BoolExpr(true), content="true")
/// inspect(ValueExpr::BoolExpr(false), content="false")
/// inspect(ValueExpr::IntExpr(1), content="1")
/// inspect(ValueExpr::IntExpr(65536), content="65536")
/// inspect(ValueExpr::FloatExpr(1.0), content="1.0")
/// inspect(ValueExpr::FloatExpr(2.0), content="2.0")
/// inspect(ValueExpr::IdentExpr(Ident("x")), content="x")
/// ```
pub(all) enum ValueExpr {
ArrayMake(Expr, Expr)
StructConstruct(Upper, Array[(Ident, Expr)]) // Point::{ x: 1, y: 2 }
EnumConstruct(Upper?, Upper, Array[Expr]) // Point(1, 2)
UnitExpr // ()
GroupExpr(Expr) // (x)
TupleExpr(Array[Expr]) // (x, y)
ArrayExpr(Array[Expr]) // [x, y, z]
BoolExpr(Bool) // true | false
IdentExpr(Ident) // x
BlockExpr(BlockExpr) // { blah; blah; blah}
NegExpr(Expr) // -x
FloatExpr(Double) // 1.0 | 1.
IntExpr(Int) // 1
NotExpr(Expr) // !x
}
///|
pub impl Show for ValueExpr with output(self, logger) {
let s = match self {
ArrayMake(size, init) => "Array::make(\{size}, \{init})"
StructConstruct(name, fields) => {
let fields_str = fields.map(f => "\{f.0}: \{f.1}").join(", ")
"\{name}::{\{fields_str}}"
}
EnumConstruct(enum_name, tag, fields) => {
let fields_str = if fields.is_empty() {
""
} else {
let s = fields.map(f => f.to_string()).join(", ")
"(\{s})"
}
let prefix = match enum_name {
Some(prefix) => "\{prefix}::"
None => ""
}
"\{prefix}\{tag}\{fields_str}"
}
UnitExpr => "()"
GroupExpr(expr) => "(\{expr})"
TupleExpr(exprs) => {
let exprs_str = exprs.map(e => e.to_string()).join(", ")
"(\{exprs_str})"
}
ArrayExpr(exprs) => {
let exprs_str = exprs.map(e => e.to_string()).join(", ")
"[\{exprs_str}]"
}
BoolExpr(value) => if value { "true" } else { "false" }
IdentExpr(ident) => ident.to_string()
BlockExpr(b) => b.to_string()
NegExpr(expr) => "-\{expr}"
FloatExpr(value) => {
let s = value.to_string()
if s.contains_char('.') {
s
} else { // e.g. "1.0"
s + ".0"
}
} // e.g. "1" -> "1.0"
IntExpr(value) => value.to_string()
NotExpr(expr) => "!\{expr}"
}
logger.write_string(s)
}
///| ApplyExpr is used to represent function application, array access, and dot access.
///
/// ```mbt
/// let complex = ValueExpr::IdentExpr(Ident("complex"));
/// let complex = ApplyExpr::ValueExpr(complex);
/// inspect(complex, content="complex")
///
/// let arr = ValueExpr::IdentExpr(Ident("arr"));
/// let arr = ApplyExpr::ValueExpr(arr);
/// inspect(arr, content="arr")
///
/// let add = ValueExpr::IdentExpr(Ident("add"));
/// let add = ApplyExpr::ValueExpr(add);
/// inspect(add, content="add")
///
/// let i1 = ValueExpr::IntExpr(1);
/// let i1 = ApplyExpr::ValueExpr(i1);
/// inspect(i1, content="1");
///
/// let i1 = IfLevelExpr::ApplyExpr(i1);
/// let i1 = Expr::IfLevelExpr(i1);
///
/// let i42 = ValueExpr::IntExpr(42);
/// let i42 = ApplyExpr::ValueExpr(i42);
/// inspect(i42, content="42");
///
/// let i42 = IfLevelExpr::ApplyExpr(i42);
/// let i42 = Expr::IfLevelExpr(i42);
///
/// inspect(ApplyExpr::ArrAcc(arr, i1), content="arr[1]")
/// inspect(ApplyExpr::DotAcc(complex, Ident("y")), content="complex.y")
/// inspect(ApplyExpr::Call(add, [i1, i42]), content="add(1, 42)")
/// ```
pub(all) enum ApplyExpr {
ValueExpr(ValueExpr)
ArrAcc(ApplyExpr, Expr)
DotAcc(ApplyExpr, Ident) // x.y
Call(ApplyExpr, Array[Expr]) // x(y1, y2, ...)
}
///|
pub impl Show for ApplyExpr with output(self, logger) {
match self {
ValueExpr(value_expr) => logger.write_string(value_expr.to_string())
ArrAcc(left, index) => logger.write_string("\{left}[\{index}]")
DotAcc(left, ident) => logger.write_string("\{left}.\{ident}")
Call(func, args) => {
let args_str = args.map(arg => arg.to_string()).join(", ")
logger.write_string("\{func}(\{args_str})")
}
}
}
///|
pub(all) enum CmpOp {
Eq
Ne
Lt
Le
Gt
Ge
}
///|
pub impl Show for CmpOp with output(self, logger) {
let s = match self {
Eq => "=="
Ne => "!="
Lt => "<"
Le => "<="
Gt => ">"
Ge => ">="
}
logger.write_string(s)
}
///| Expr is the main expression type that can be used in the AST.
///
/// ```mbt
/// let x = ValueExpr::IdentExpr(Ident("x"));
/// let y = ValueExpr::IdentExpr(Ident("y"));
/// let i42 = ValueExpr::IntExpr(42);
/// let i73 = ValueExpr::IntExpr(73);
/// let f22 = ValueExpr::FloatExpr(22.0);
/// let f89 = ValueExpr::FloatExpr(89.0);
/// let bool_true = ValueExpr::BoolExpr(true);
/// let bool_false = ValueExpr::BoolExpr(false);
///
/// let x = ApplyExpr::ValueExpr(x);
/// let x = IfLevelExpr::ApplyExpr(x);
/// let x = Expr::IfLevelExpr(x);
///
/// let y = ApplyExpr::ValueExpr(y);
/// let y = IfLevelExpr::ApplyExpr(y);
/// let y = Expr::IfLevelExpr(y);
///
/// let i42 = ApplyExpr::ValueExpr(i42);
/// let i42 = IfLevelExpr::ApplyExpr(i42);
/// let i42 = Expr::IfLevelExpr(i42);
///
/// let i73 = ApplyExpr::ValueExpr(i73);
/// let i73 = IfLevelExpr::ApplyExpr(i73);
/// let i73 = Expr::IfLevelExpr(i73);
///
/// let f22 = ApplyExpr::ValueExpr(f22);
/// let f22 = IfLevelExpr::ApplyExpr(f22);
/// let f22 = Expr::IfLevelExpr(f22);
///
/// let f89 = ApplyExpr::ValueExpr(f89);
/// let f89 = IfLevelExpr::ApplyExpr(f89);
/// let f89 = Expr::IfLevelExpr(f89);
///
/// let bool_true = ApplyExpr::ValueExpr(bool_true);
/// let bool_true = IfLevelExpr::ApplyExpr(bool_true);
/// let bool_true = Expr::IfLevelExpr(bool_true);
///
/// let bool_false = ApplyExpr::ValueExpr(bool_false);
/// let bool_false = IfLevelExpr::ApplyExpr(bool_false);
/// let bool_false = Expr::IfLevelExpr(bool_false);
///
/// inspect(Expr::AndExpr(bool_false, y), content="false && y")
/// inspect(Expr::OrExpr(x, bool_true), content="x || true")
/// inspect(Expr::CmpExpr(Eq, x, y), content="x == y")
/// inspect(Expr::CmpExpr(Ne, x, y), content="x != y")
/// inspect(Expr::CmpExpr(Lt, i42, i73), content="42 < 73")
/// inspect(Expr::CmpExpr(Le, i42, i73), content="42 <= 73")
/// inspect(Expr::CmpExpr(Gt, i73, i42), content="73 > 42")
/// inspect(Expr::CmpExpr(Ge, i73, i42), content="73 >= 42")
/// inspect(Expr::AddExpr(i42, i73), content="42 + 73")
/// inspect(Expr::SubExpr(i73, i42), content="73 - 42")
/// inspect(Expr::MulExpr(i42, i73), content="42 * 73")
/// inspect(Expr::DivExpr(i73, i42), content="73 / 42")
/// inspect(Expr::ModExpr(i73, i42), content="73 % 42")
/// ```
pub(all) enum Expr {
AndExpr(Expr, Expr) // x && y
OrExpr(Expr, Expr) // x || y
CmpExpr(CmpOp, Expr, Expr) // x == y | x != y | x < y | x <= y | x > y | x >= y
AddExpr(Expr, Expr) // x + y
SubExpr(Expr, Expr) // x - y
MulExpr(Expr, Expr) // x * y
DivExpr(Expr, Expr) // x / y
ModExpr(Expr, Expr) // x % y
IfLevelExpr(IfLevelExpr)
}
///|
pub impl Show for Expr with output(self, logger) {
let s = match self {
AndExpr(left, right) => "\{left} && \{right}"
OrExpr(left, right) => "\{left} || \{right}"
CmpExpr(op, left, right) => "\{left} \{op} \{right}"
AddExpr(left, right) => "\{left} + \{right}"
SubExpr(left, right) => "\{left} - \{right}"
MulExpr(left, right) => "\{left} * \{right}"
DivExpr(left, right) => "\{left} / \{right}"
ModExpr(left, right) => "\{left} % \{right}"
IfLevelExpr(if_level_expr) => if_level_expr.to_string()
}
logger.write_string(s)
}
///| IfLevelExpr represents expressions that have the same precedence as if expressions.
///
/// ```mbt
/// let x = ValueExpr::IdentExpr(Ident("x"));
/// let x = ApplyExpr::ValueExpr(x);
/// inspect(IfLevelExpr::ApplyExpr(x), content="x")
///
/// let cond = ValueExpr::BoolExpr(true);
/// let cond = ApplyExpr::ValueExpr(cond);
/// let cond = IfLevelExpr::ApplyExpr(cond);
/// let cond = Expr::IfLevelExpr(cond);
/// let then_block = BlockExpr::new()
/// let else_block = BlockExpr::new()
/// let if_expr = IfExpr::{ cond, then_: then_block, else_: Right(else_block) };
/// inspect(IfLevelExpr::IfExpr(if_expr), content="if true {\n} else {\n}")
///
/// let match_expr = MatchExpr::{ nested_level: 0, expr: cond, arms: [] };
/// inspect(IfLevelExpr::MatchExpr(match_expr), content="match true {\n}")
/// ```
pub(all) enum IfLevelExpr {
ApplyExpr(ApplyExpr)
IfExpr(IfExpr)
MatchExpr(MatchExpr)
}
///|
pub impl Show for IfLevelExpr with output(self, logger) {
match self {
ApplyExpr(apply_expr) => logger.write_string(apply_expr.to_string())
IfExpr(if_expr) => logger.write_string(if_expr.to_string())
MatchExpr(match_expr) => logger.write_string(match_expr.to_string())
}
}
///| IfExpr represents conditional expressions with if-then-else structure.
///
/// ```mbt
/// let cond = ValueExpr::BoolExpr(true);
/// let cond = ApplyExpr::ValueExpr(cond);
/// let cond = IfLevelExpr::ApplyExpr(cond);
/// let cond = Expr::IfLevelExpr(cond);
///
/// let x_val = ValueExpr::IntExpr(42);
/// let x_val = ApplyExpr::ValueExpr(x_val);
/// let x_val = IfLevelExpr::ApplyExpr(x_val);
/// let x_val = Expr::IfLevelExpr(x_val);
///
/// let y_val = ValueExpr::IntExpr(24);
/// let y_val = ApplyExpr::ValueExpr(y_val);
/// let y_val = IfLevelExpr::ApplyExpr(y_val);
/// let y_val = Expr::IfLevelExpr(y_val);
///
/// let then_block = BlockExpr::new(last_expr=Some(x_val));
/// let else_block = BlockExpr::new(last_expr=Some(y_val));
/// let if_expr = IfExpr::{ cond, then_: then_block, else_: Right(else_block) };
///
/// let expect =
/// #|if true {
/// #| 42
/// #|} else {
/// #| 24
/// #|}
///
/// inspect(if_expr, content=expect)
/// ```
pub(all) struct IfExpr {
cond : Expr
then_ : BlockExpr
else_ : Either[IfExpr, BlockExpr]
}
///|
pub impl Show for IfExpr with output(self, logger) {
logger.write_string("if \{self.cond} ")
logger.write_string(self.then_.to_string())
match self.else_ {
Left(if_expr) => {
logger.write_string(" else ")
logger.write_string(if_expr.to_string())
}
Right(block) => {
logger.write_string(" else ")
logger.write_string(block.to_string())
}
}
}
///| BlockExpr represents a block of code wrapped in `{}` containing statements and an optional final expression.
/// A block can contain multiple statements followed by an optional expression that serves as the block's value.
/// The nested_level field controls indentation for pretty printing.
///
/// ```mbt
/// // Simple block with just statements
/// let i42 = ValueExpr::IntExpr(42);
/// let i42 = ApplyExpr::ValueExpr(i42);
/// let i42 = IfLevelExpr::ApplyExpr(i42);
/// let i42 = Expr::IfLevelExpr(i42);
///
/// let stmt = Stmt::Let(Ident("x"), None, i42);
/// let block1 = BlockExpr::new()
/// block1.push(stmt);
///
/// let expect1 =
/// #|{
/// #| let x = 42;
/// #|}
///
/// inspect(block1, content=expect1)
///
/// // Block with statements and final expression
/// let y_val = ValueExpr::IntExpr(100);
/// let y_val = ApplyExpr::ValueExpr(y_val);
/// let y_val = IfLevelExpr::ApplyExpr(y_val);
/// let y_val = Expr::IfLevelExpr(y_val);
///
/// let stmt2 = Stmt::Let(Ident("y"), None, y_val);
/// let final_expr = ValueExpr::IdentExpr(Ident("y"));
/// let final_expr = ApplyExpr::ValueExpr(final_expr);
/// let final_expr = IfLevelExpr::ApplyExpr(final_expr);
/// let final_expr = Expr::IfLevelExpr(final_expr);
///
/// let block2 = BlockExpr::new(last_expr = Some(final_expr));
/// block2.push(stmt2);
///
///
/// let expect2 =
/// #|{
/// #| let y = 100;
/// #| y
/// #|}
///
/// inspect(block2, content=expect2)
///
/// // Empty block
/// let empty_block = BlockExpr::new()
/// inspect(empty_block, content="{\n}")
/// ```
pub struct BlockExpr {
nested_level : Int // for print indentation
stmts : Array[Stmt]
mut last_expr : Expr?
parent : BlockExpr?
}
///|
pub fn BlockExpr::new(
nested_level~ : Int = 0,
parent~ : BlockExpr? = None,
last_expr~ : Expr? = None,
) -> BlockExpr {
BlockExpr::{ nested_level, stmts: [], last_expr, parent }
}
///|
pub fn BlockExpr::set_last_expr(self : Self, expr : Expr) -> Unit {
self.last_expr = Some(expr)
}
///|
pub fn BlockExpr::push(self : Self, stmt : Stmt) -> Unit {
self.stmts.push(stmt)
}
///|
pub impl Show for BlockExpr with output(self, logger) {
// no ident before the first LBrace
logger.write_string("{\n")
let indent = " ".repeat(self.nested_level + 1)
for stmt in self.stmts {
logger.write_string(indent)
logger.write_string(stmt.to_string())
logger.write_string("\n")
}
if self.last_expr is Some(expr) {
logger.write_string(indent)
logger.write_string(expr.to_string())
logger.write_string("\n")
}
let last_indent = " ".repeat(self.nested_level)
logger.write_string(last_indent)
logger.write_string("}")
}
///| MatchExpr represents pattern matching expressions with multiple arms.
///
/// ```mbt
/// let x = ValueExpr::IdentExpr(Ident("x"));
/// let x = ApplyExpr::ValueExpr(x);
/// let x = IfLevelExpr::ApplyExpr(x);
/// let x = Expr::IfLevelExpr(x);
///
/// let val1 = ValueExpr::IntExpr(1);
/// let val1 = ApplyExpr::ValueExpr(val1);
/// let val1 = IfLevelExpr::ApplyExpr(val1);
/// let val1 = Expr::IfLevelExpr(val1);
///
/// let val2 = ValueExpr::IntExpr(2);
/// let val2 = ApplyExpr::ValueExpr(val2);
/// let val2 = IfLevelExpr::ApplyExpr(val2);
/// let val2 = Expr::IfLevelExpr(val2);
///
/// let arms = [
/// (Pattern::Number(1), val1),
/// (Pattern::WildCard, val2)
/// ];
/// let match_expr = MatchExpr::{ nested_level: 0, expr: x, arms };
///
/// let expect =
/// #|match x {
/// #| 1 => 1,
/// #| _ => 2,
/// #|}
///
/// inspect(match_expr, content=expect)
/// ```
pub struct MatchExpr {
nested_level : Int // for print indentation
expr : Expr
arms : Array[(Pattern, Expr)]
}
///|
pub impl Show for MatchExpr with output(self, logger) {
// no ident before the first LBrace
logger.write_string("match \{self.expr} {\n")
let indent = " ".repeat(self.nested_level + 1)
for arm in self.arms {
let (pattern, expr) = arm
logger.write_string(indent)
logger.write_string("\{pattern} => \{expr};\n")
}
let last_indent = " ".repeat(self.nested_level)
logger.write_string(last_indent)
logger.write_string("}")
}
///| Pattern is used in the match arm.
///
/// ```mbt
/// inspect(Pattern::Number(1), content="1")
/// inspect(Pattern::Bool(true), content="true")
/// inspect(Pattern::WildCard, content="_")
/// inspect(Pattern::Ident("x"), content="x")
/// inspect(Pattern::Tuple([Ident("x"), Ident("y")]), content="(x, y)")
/// inspect(Pattern::EnumPattern(None, "Point", [Ident("x"), Ident("y")]), content="Point(x, y)")
/// inspect(Pattern::EnumPattern(None, "Red", []), content="Red")
/// inspect(Pattern::EnumPattern(Some("Color"), "Red", []), content="Color::Red")
/// ```
pub(all) enum Pattern {
Number(Int) // 1, 2, ...
Bool(Bool) // true, false
WildCard // _
Ident(Ident) // x, y, ...
Tuple(Array[Pattern]) // (x, y, z)
EnumPattern(Upper?, Upper, Array[Pattern])
}
///|
pub impl Show for Pattern with output(self, logger) {
match self {
Number(value) => logger.write_string(value.to_string())
Bool(value) => logger.write_string(if value { "true" } else { "false" })
WildCard => logger.write_string("_")
Ident(ident) => logger.write_string(ident.to_string())
Tuple(patterns) => {
let patterns_str = patterns.map(p => p.to_string()).join(", ")
logger.write_string("(\{patterns_str})")
}
EnumPattern(prefix_opt, name, patterns) => {
let prefix_str = match prefix_opt {
Some(prefix) => "\{prefix}::"
None => ""
}
let patterns_str = if patterns.is_empty() {
""
} else {
let s = patterns.map(p => p.to_string()).join(", ")
"(\{s})"
}
logger.write_string("\{prefix_str}\{name}\{patterns_str}")
}
}
}
///| Stmt represents different types of statements in the language.
/// Statements are instructions that perform actions but do not return values.
///
/// ```mbt
/// // Let statement with type annotation
/// let val42 = ValueExpr::IntExpr(42);
/// let val42 = ApplyExpr::ValueExpr(val42);
/// let val42 = IfLevelExpr::ApplyExpr(val42);
/// let val42 = Expr::IfLevelExpr(val42);
/// let let_stmt = Stmt::Let(Ident("x"), Some(Type::Int), val42);
/// inspect(let_stmt, content="let x: Int = 42;")
///
/// // Mutable let statement
/// let mut_stmt = Stmt::LetMut(Ident("counter"), None, val42);
/// inspect(mut_stmt, content="let mut counter = 42;")
///
/// // Tuple destructuring let
/// let bindings = [Binding::Ident(Ident("a")), Binding::Ident(Ident("b"))];
/// let tuple_val = ValueExpr::TupleExpr([val42, val42]);
/// let tuple_val = ApplyExpr::ValueExpr(tuple_val);
/// let tuple_val = IfLevelExpr::ApplyExpr(tuple_val);
/// let tuple_val = Expr::IfLevelExpr(tuple_val);
/// let tuple_stmt = Stmt::LetTuple(bindings, None, tuple_val);
/// inspect(tuple_stmt, content="let (a, b) = (42, 42);")
///
/// // Assignment statement
/// let left_val = LeftValue::Ident(Ident("x"));
/// let assign_stmt = Stmt::Assign(left_val, val42);
/// inspect(assign_stmt, content="x = 42;")
///
/// // While loop
/// let cond = ValueExpr::BoolExpr(true);
/// let cond = ApplyExpr::ValueExpr(cond);
/// let cond = IfLevelExpr::ApplyExpr(cond);
/// let cond = Expr::IfLevelExpr(cond);
/// let body = BlockExpr::new()
/// let while_stmt = Stmt::While(cond, body);
/// inspect(while_stmt, content="while true {\n}")
///
/// // Return statement
/// let return_stmt = Stmt::Return(val42);
/// inspect(return_stmt, content="return 42;")
///
/// // Expression statement
/// let expr_stmt = Stmt::ExprStmt(val42);
/// inspect(expr_stmt, content="42;")
/// ```
pub(all) enum Stmt {
LetTuple(Array[Binding], Type?, Expr)
LetMut(Ident, Type?, Expr)
Let(Ident, Type?, Expr)
LocalFuncDef(Ident, Array[(Ident, Type?)], Type?, BlockExpr)
Assign(LeftValue, Expr)
While(Expr, BlockExpr)
Return(Expr)
ExprStmt(Expr)
}
///|
pub impl Show for Stmt with output(self, logger) {
match self {
LetTuple(bindings, ty_opt, expr) => {
let bindings_str = bindings.map(b => b.to_string()).join(", ")
let ty_str = match ty_opt {
Some(ty) => ": \{ty}"
None => ""
}
logger.write_string("let (\{bindings_str})\{ty_str} = \{expr};")
}
LetMut(ident, ty_opt, expr) => {
let ty_str = match ty_opt {
Some(ty) => ": \{ty}"
None => ""
}
logger.write_string("let mut \{ident}\{ty_str} = \{expr};")
}
Let(ident, ty_opt, expr) => {
let ty_str = match ty_opt {
Some(ty) => ": \{ty}"
None => ""
}
logger.write_string("let \{ident}\{ty_str} = \{expr};")
}
LocalFuncDef(fname, params, ret_ty_opt, body) => {
let params_str = params
.map(fn(p) {
let (name, ty_opt) = p
match ty_opt {
Some(ty) => "\{name}: \{ty}"
None => name.to_string()
}
})
.join(", ")
let ret_ty_str = match ret_ty_opt {
Some(ret_ty) => " -> \{ret_ty}"
None => ""
}
logger.write_string("fn \{fname}(\{params_str})\{ret_ty_str} ")
logger.write_string(body.to_string())
}
Assign(left_value, expr) => logger.write_string("\{left_value} = \{expr};")
While(cond, body) => {
logger.write_string("while \{cond} ")
logger.write_string(body.to_string())
}
Return(expr) => logger.write_string("return \{expr};")
ExprStmt(expr) => {
//logger.write_string("let _ = \{expr};");
let prefix = match expr {
IfLevelExpr(iexpr) =>
match iexpr {
ApplyExpr(_) => "let _ = "
_ => ""
}
_ => "let _ = "
}
logger.write_string("\{prefix}\{expr};")
}
}
}
///| Binding is used in the left-hand side of a `let` statement.
///
/// ```mbt
/// inspect(Binding::Ident("x"), content="x")
/// inspect(Binding::WhileCard, content="_")
/// ```
pub(all) enum Binding {
Ident(Ident)
WhileCard
}
///|
pub impl Show for Binding with output(self, logger) {
let s = match self {
Ident(ident) => ident.to_string()
WhileCard => "_".to_string()
}
logger.write_string(s)
}
///| LeftValue is the left-hand side of an assignment.
///
/// ```mbt
/// inspect(LeftValue::Ident("x"), content="x")
/// inspect(LeftValue::DotAcc(Ident("x"), "y"), content="x.y")
///
/// let int_expr = ValueExpr::IntExpr(1);
/// let int_expr = ApplyExpr::ValueExpr(int_expr);
/// let int_expr = IfLevelExpr::ApplyExpr(int_expr);
/// let int_expr = Expr::IfLevelExpr(int_expr);
/// inspect(LeftValue::ArrAcc(Ident("arr"), int_expr), content="arr[1]")
/// ```
pub(all) enum LeftValue {
Ident(Ident)
DotAcc(LeftValue, Ident) // x.y
ArrAcc(LeftValue, Expr) // x[y]
}
///|
pub impl Show for LeftValue with output(self, logger) {
let s = match self {
Ident(ident) => ident.to_string()
DotAcc(left, ident) => "\{left}.\{ident}"
ArrAcc(left, index) => "\{left}[\{index}]"
}
logger.write_string(s)
}
///| TopLet represents top-level let bindings with optional type annotations.
/// These are global variable declarations that can be used throughout the program.
///
/// ```mbt
/// let val42 = ValueExpr::IntExpr(42);
/// let val42 = ApplyExpr::ValueExpr(val42);
/// let val42 = IfLevelExpr::ApplyExpr(val42);
/// let val42 = Expr::IfLevelExpr(val42);
///
/// // Top-level let without type annotation
/// let top_let1 = TopLet::{ name: "global_var", ty: None, value: val42 };
/// inspect(top_let1, content="let global_var = 42;")
///
/// // Top-level let with type annotation
/// let top_let2 = TopLet::{ name: "typed_var", ty: Some(Type::Int), value: val42 };
/// inspect(top_let2, content="let typed_var: Int = 42;")
///
/// // Top-level let with complex expression
/// let array_val = ValueExpr::ArrayExpr([val42, val42]);
/// let array_val = ApplyExpr::ValueExpr(array_val);
/// let array_val = IfLevelExpr::ApplyExpr(array_val);
/// let array_val = Expr::IfLevelExpr(array_val);
/// let top_let3 = TopLet::{ name:"array_var", ty: Some(Type::Array(Type::Int)), value: array_val };
/// inspect(top_let3, content="let array_var: Array[Int] = [42, 42];")
/// ```
pub(all) struct TopLet {
name : Ident
ty : Type?
value : Expr
}
///|
pub impl Show for TopLet with output(self, logger) {
let ty_str = match self.ty {
Some(ty) => ": \{ty}"
None => ""
}
logger.write_string("let \{self.name}\{ty_str} = \{self.value};")
}
///| TopFuncDef represents top-level function definitions with optional generic parameters.
/// These are function declarations that can be called from anywhere in the program.
///
/// ```mbt
/// let val42 = ValueExpr::IntExpr(42);
/// let val42 = ApplyExpr::ValueExpr(val42);
/// let val42 = IfLevelExpr::ApplyExpr(val42);
/// let val42 = Expr::IfLevelExpr(val42);
/// let body = BlockExpr::new(last_expr=Some(val42));
///
/// // Simple function without generic parameters
/// let params1 : Array[(Ident, Type)] = [("x", Int), ("y", Int)];
/// let func1 = TopFuncDef::{
/// generic_param: None,
/// name: "add",
/// params: params1,
/// ret_ty: Type::Int,
/// body
/// };
/// inspect(func1, content="fn add(x: Int, y: Int) -> Int {\n 42\n}")
///
/// // Generic function with type parameter
/// let params2: Array[(Ident, Type)] = [("x", GenericDef("T"))];
/// let func2 = TopFuncDef::{
/// generic_param: Some(Upper("T")),
/// name: "identity",
/// params: params2,
/// ret_ty: Type::GenericDef(Upper("T")),
/// body
/// };
/// inspect(func2, content="fn[T] identity(x: T) -> T {\n 42\n}")
///
/// // Function with no parameters
/// let func3 = TopFuncDef::{
/// generic_param: None,
/// name: "get_answer",
/// params: [],
/// ret_ty: Type::Int,
/// body
/// };
/// inspect(func3, content="fn get_answer() -> Int {\n 42\n}")
/// ```
pub(all) struct TopFuncDef {
generic_param : Upper?
name : Ident
params : Array[(Ident, Type)]
ret_ty : Type
body : BlockExpr
}
///|
pub impl Show for TopFuncDef with output(self, logger) {
let generic_str = match self.generic_param {
Some(param) => "[\{param}]"
None => ""
}
let params_str = self.params.map(p => "\{p.0}: \{p.1}").join(", ")
if self.name != "main" {
logger.write_string(
"fn\{generic_str} \{self.name}(\{params_str}) -> \{self.ret_ty} ",
)
} else {
logger.write_string("fn main ")
}
logger.write_string(self.body.to_string())
}
///| StructDef represents struct type definitions with optional generic parameters.
/// Structs define custom data types with named fields.
///
/// ```mbt
/// // Simple struct without generic parameters
/// let fields1 : Array[(Ident, Type)] = [("x", Int), ("y", Int)];
/// let struct1 = StructDef::{
/// generic_param: None,
/// name: Upper("Point"),
/// fields: fields1
/// };
/// let expect1 =
/// #|struct Point {
/// #| x: Int
/// #| y: Int
/// #|}
/// inspect(struct1, content=expect1)
///
/// // Generic struct with type parameter
/// let fields2: Array[(Ident, Type)] = [("value", GenericDef("T"))];
/// let struct2 = StructDef::{
/// generic_param: Some(Upper("T")),
/// name: Upper("Container"),
/// fields: fields2
/// };
/// let expect2 =
/// #|struct Container[T] {
/// #| value: T
/// #|}
/// inspect(struct2, content=expect2)
///
/// // Empty struct
/// let struct3 = StructDef::{
/// generic_param: None,
/// name: Upper("Empty"),
/// fields: []
/// };
/// inspect(struct3, content="struct Empty {}\n")
/// ```
pub(all) struct StructDef {
generic_param : Upper?
name : Upper
fields : Array[(Ident, Type)]
}
///|
pub fn StructDef::type_of(self : Self) -> Type {
Type::Struct(self.name, self.fields)
}
///|
pub impl Show for StructDef with output(self, logger) {
let generic_str = match self.generic_param {
Some(param) => "[\{param}]"
None => ""
}
logger.write_string("struct \{self.name}\{generic_str} {")
if self.fields.is_empty() {
logger.write_string("}\n")
return
}
logger.write_string("\n")
let indent = " "
for field in self.fields {
let (name, ty) = field
logger.write_string(indent)
logger.write_string("\{name}: \{ty};\n")
}
logger.write_string("}")
}
///| EnumDef represents enum type definitions with optional generic parameters.
/// Enums define algebraic data types with multiple variants that can have associated data.
///
/// ```mbt
/// // Simple enum without generic parameters and with various variant types
/// let variants1 : Array[(Upper, Array[Type])] = [
/// ("Red", []),
/// ("Green", []),
/// ("Blue", []),
/// ("RGB", [Int, Int, Int])
/// ];
/// let enum1 = EnumDef::{
/// generic_param: None,
/// name: Upper("Color"),
/// variants: variants1
/// };
/// let expect1 =
/// #|enum Color {
/// #| Red
/// #| Green
/// #| Blue
/// #| RGB(Int, Int, Int)
/// #|}
/// inspect(enum1, content=expect1)
///
/// // Generic enum with type parameter
/// let variants2 : Array[(Upper, Array[Type])] = [
/// ("None", []),
/// ("Some", [GenericDef("T")])
/// ];
/// let enum2 = EnumDef::{
/// generic_param: Some(Upper("T")),
/// name: Upper("Option"),
/// variants: variants2
/// };
/// let expect2 =
/// #|enum Option[T] {
/// #| None
/// #| Some(T)
/// #|}
/// inspect(enum2, content=expect2)
///
/// // Empty enum
/// let enum3 = EnumDef::{
/// generic_param: None,
/// name: Upper("Never"),
/// variants: []
/// };
/// inspect(enum3, content="enum Never {}\n")
/// ```
pub(all) struct EnumDef {
generic_param : Upper?
name : Upper
variants : Array[(Upper, Array[Type])] // e.g. Point(x: Int, y: Int)
}
///|
pub fn EnumDef::type_of(self : Self) -> Type {
Type::Enum(self.name, self.variants)
}
///|
pub impl Show for EnumDef with output(self, logger) {
let generic_str = match self.generic_param {
Some(param) => "[\{param}]"
None => ""
}
logger.write_string("enum \{self.name}\{generic_str} {")
if self.variants.is_empty() {
logger.write_string("}\n")
return
}
logger.write_string("\n")
let indent = " "
for variant in self.variants {
let (name, fields) = variant
logger.write_string(indent)
logger.write_string("\{name}")
if fields.is_empty() {
logger.write_string(";\n")
continue
}
let fields_str = fields.map(f => f.to_string()).join(", ")
logger.write_string("(\{fields_str})")
logger.write_string(";\n")
}
logger.write_string("}")
}
///|
pub enum TopDecl {
TopLet(TopLet)
TopFuncDef(TopFuncDef)
StructDef(StructDef)
EnumDef(EnumDef)
}
///|
pub impl Show for TopDecl with output(self, logger) {
match self {
TopLet(top_let) => logger.write_string(top_let.to_string())
TopFuncDef(top_func_def) => logger.write_string(top_func_def.to_string())
StructDef(struct_def) => logger.write_string(struct_def.to_string())
EnumDef(enum_def) => logger.write_string(enum_def.to_string())
}
}
///|
pub struct Program {
top_decls : Array[TopDecl]
}
///|
pub impl Show for Program with output(self, logger) {
for decl in self.top_decls {
logger.write_string(decl.to_string())
logger.write_string("\n\n")
}
}