feat: knf

This commit is contained in:
2025-11-11 05:28:39 +08:00
parent c850ceb3b6
commit b7cbbdfbba
25 changed files with 2654 additions and 23 deletions

View File

@@ -1,6 +1,8 @@
# Lilunar # Lilunar
Lil-Ran's MiniMoonBit to RISC-V compiler for [MGPIC-2025](https://www.moonbitlang.cn/2025-mgpic-compiler). 🚧 **To be continued...**
Lil-Ran's MiniMoonBit to LLVM IR compiler for [MGPIC-2025](https://www.moonbitlang.cn/2025-mgpic-compiler).
It is not optimized yet. It is not optimized yet.

View File

@@ -32,7 +32,7 @@ async def main():
for file in os.listdir("contest-2025-data/test_cases/mbt"): for file in os.listdir("contest-2025-data/test_cases/mbt"):
if file.endswith(".mbt"): if file.endswith(".mbt"):
in_path = os.path.join("contest-2025-data/test_cases/mbt", file) in_path = os.path.join("contest-2025-data/test_cases/mbt", file)
out_path = os.path.join("output/repo", file.replace(".mbt", ".s")) out_path = os.path.join("output/repo", file.replace(".mbt", ".ll"))
tasks.append(check_file(in_path, out_path)) tasks.append(check_file(in_path, out_path))
await asyncio.gather(*tasks) await asyncio.gather(*tasks)

View File

@@ -1,3 +1,3 @@
{ {
"emit": "asm" "emit": "llvm"
} }

View File

@@ -11,10 +11,9 @@
"keywords": [ "keywords": [
"MiniMoonBit", "MiniMoonBit",
"compiler", "compiler",
"RISC-V", "MGPIC-2025"
"assembly"
], ],
"description": "My MiniMoonBit to RISC-V compiler for MGPIC-2025.", "description": "Lil-Ran's MiniMoonBit to LLVM IR compiler for MGPIC-2025.",
"source": "src", "source": "src",
"preferred-target": "wasm-gc" "preferred-target": "wasm-gc"
} }

View File

@@ -42,7 +42,7 @@ fn main {
in_file = Some(s) in_file = Some(s)
}, },
( (
#|Lilunar: Lil-Ran's experimental MiniMoonBit to RISC-V compiler for MGPIC-2025. #|Lilunar: Lil-Ran's experimental MiniMoonBit to LLVM IR compiler for MGPIC-2025.
#| #|
#| usage: lilunar [options] <input-file> #| usage: lilunar [options] <input-file>
#| #|
@@ -61,7 +61,7 @@ fn main {
} }
println_debug( println_debug(
( (
#| #|Source:
#|================================ #|================================
$|\{contents} $|\{contents}
#|================================ #|================================
@@ -82,11 +82,25 @@ fn main {
println_debug("Type checking passed.") println_debug("Type checking passed.")
return return
} }
let knf = @knf.knf_transform(program) catch {
// let asm_string = ... @knf.KnfTransformError(msg) =>
// if out_file.val == "-" { println_panic("KNF transformation error: \{msg}")
// println(asm_string) }
// } else { println_debug(
// @fs.write_string_to_file?(out_file.val, asm_string).unwrap() (
// } #|KNF:
#|================================
$|\{knf}
#|================================
),
)
let output_string = knf.to_string()
if out_file.val == "-" {
println(output_string)
} else {
@fs.write_string_to_file(out_file.val, output_string) catch {
@fs.IOError(msg) =>
println_panic("Failed to write to output file \{out_file.val}: \{msg}")
}
}
} }

View File

@@ -5,6 +5,7 @@
"Yoorkin/ArgParser", "Yoorkin/ArgParser",
"moonbitlang/x/fs", "moonbitlang/x/fs",
"Lil-Ran/lilunar/parser", "Lil-Ran/lilunar/parser",
"Lil-Ran/lilunar/typecheck" "Lil-Ran/lilunar/typecheck",
"Lil-Ran/lilunar/knf"
] ]
} }

150
src/knf/apply_expr.mbt Normal file
View File

@@ -0,0 +1,150 @@
///|
pub fn Context::apply_expr_to_knf(
self : Context,
apply_expr : @typecheck.ApplyExpr,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
match apply_expr.kind {
AtomExpr(atom_expr) => self.atom_expr_to_knf(atom_expr)
ArrayAccess(array_expr, index_expr) => {
let stmts = []
let (array_stmts, array_knf_expr) = self.apply_expr_to_knf(array_expr)
stmts.append(array_stmts)
let array_name = self.expr_to_knf_name(
array_knf_expr,
self.typekind_to_knf(array_expr.ty),
stmts,
)
let (index_stmts, index_knf_expr) = self.expr_to_knf(index_expr)
stmts.append(index_stmts)
let index_name = self.expr_to_knf_name(
index_knf_expr,
self.typekind_to_knf(index_expr.ty),
stmts,
)
let knf_expr = ArrayAccess(array_name, index_name)
(stmts, knf_expr)
}
FieldAccess(struct_expr, field_name) => {
let stmts = []
let (struct_stmts, struct_knf_expr) = self.apply_expr_to_knf(struct_expr)
stmts.append(struct_stmts)
let struct_name = self.expr_to_knf_name(
struct_knf_expr,
self.typekind_to_knf(struct_expr.ty),
stmts,
)
let knf_expr = FieldAccess(struct_name, field_name)
(stmts, knf_expr)
}
Call(callee_expr, arg_exprs) => {
let stmts = []
let (callee_stmts, callee_knf_expr) = self.apply_expr_to_knf(callee_expr)
stmts.append(callee_stmts)
let callee_name = self.expr_to_knf_name(
callee_knf_expr,
self.typekind_to_knf(callee_expr.ty),
stmts,
)
let arg_names = []
for arg_expr in arg_exprs {
let (arg_stmts, arg_knf_expr) = self.expr_to_knf(arg_expr)
stmts.append(arg_stmts)
let arg_name = self.expr_to_knf_name(
arg_knf_expr,
self.typekind_to_knf(arg_expr.ty),
stmts,
)
arg_names.push(arg_name)
}
let knf_expr = Call(callee_name, arg_names)
(stmts, knf_expr)
}
}
}
///|
pub fn Context::atom_expr_to_knf(
self : Context,
atom_expr : @typecheck.AtomExpr,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
let { kind, ty } = atom_expr
match kind {
Int(v) => ([], Int(v))
Double(v) => ([], Double(v))
Bool(v) => ([], Bool(v))
Unit => ([], Unit)
Ident(s) => {
guard self.lookup_name(s) is Some((name, _)) else {
raise KnfTransformError("undefined identifier in atom expression: \{s}")
}
([], Ident(name))
}
Array(elems) => {
guard ty is Array(elem_ty) else {
raise KnfTransformError("expected array type in array literal")
}
let stmts = []
let elem_names = []
for elem in elems {
let (elem_stmts, elem_expr) = self.expr_to_knf(elem)
stmts.append(elem_stmts)
let elem_name = self.expr_to_knf_name(
elem_expr,
self.typekind_to_knf(elem.ty),
stmts,
)
elem_names.push(elem_name)
}
(stmts, ArrayLiteral(self.typekind_to_knf(elem_ty), elem_names))
}
Tuple(elems) => {
let stmts = []
let elem_names = []
for elem in elems {
let (elem_stmts, elem_expr) = self.expr_to_knf(elem)
stmts.append(elem_stmts)
let elem_name = self.expr_to_knf_name(
elem_expr,
self.typekind_to_knf(elem.ty),
stmts,
)
elem_names.push(elem_name)
}
(stmts, TupleLiteral(elem_names))
}
ArrayMake(size_expr, init_expr) => {
let stmts = []
let (size_stmts, size_knf_expr) = self.expr_to_knf(size_expr)
stmts.append(size_stmts)
let size_name = self.expr_to_knf_name(
size_knf_expr,
self.typekind_to_knf(size_expr.ty),
stmts,
)
let (init_stmts, init_knf_expr) = self.expr_to_knf(init_expr)
stmts.append(init_stmts)
let init_name = self.expr_to_knf_name(
init_knf_expr,
self.typekind_to_knf(init_expr.ty),
stmts,
)
(stmts, ArrayMake(size_name, init_name))
}
StructConstruct({ name, fields }) => {
let stmts = []
let init_names = []
for field in fields {
let (field_name, field_expr) = field
let (field_stmts, field_knf_expr) = self.expr_to_knf(field_expr)
stmts.append(field_stmts)
let field_name_knf = self.expr_to_knf_name(
field_knf_expr,
self.typekind_to_knf(field_expr.ty),
stmts,
)
init_names.push((field_name, field_name_knf))
}
(stmts, CreateStruct(name, init_names))
}
}
}

67
src/knf/assign_stmt.mbt Normal file
View File

@@ -0,0 +1,67 @@
///|
pub fn Context::assign_stmt_to_knf(
self : Context,
assign_stmt : @typecheck.AssignStmt,
) -> Array[KnfStmt] raise KnfTransformError {
let { left_value, expr } = assign_stmt
let stmts = []
let (expr_stmts, expr_knf_expr) = self.expr_to_knf(expr)
stmts.append(expr_stmts)
let expr_ty = self.typekind_to_knf(expr.ty)
let (left_value_stmts, left_value_knf_expr) = self.left_value_to_knf(
left_value,
)
stmts.append(left_value_stmts)
match left_value_knf_expr {
Ident(name) => stmts.push(Assign(name, expr_knf_expr))
ArrayAccess(array_name, index_name) =>
stmts.push(ArrayPut(array_name, index_name, expr_knf_expr))
FieldAccess(struct_name, field_name) =>
stmts.push(
StructFieldSet(
struct_name,
field_name,
self.expr_to_knf_name(expr_knf_expr, expr_ty, stmts),
),
)
_ => panic() // left_value_to_knf should not produce other kinds
}
stmts
}
///|
pub fn Context::left_value_to_knf(
self : Context,
left_value : @typecheck.LeftValue,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
match left_value.kind {
Ident(name_str) => {
let (name, _) = self
.lookup_name(name_str)
.or_error(
KnfTransformError("undefined identifier in left value: \{name_str}"),
)
([], Ident(name))
}
ArrayAccess(array_expr, index_expr) => {
let stmts = []
let (array_stmts, array_knf_expr) = self.left_value_to_knf(array_expr)
stmts.append(array_stmts)
let array_ty = self.type_to_knf(array_expr.ty)
let array_name = self.expr_to_knf_name(array_knf_expr, array_ty, stmts)
let (index_stmts, index_knf_expr) = self.expr_to_knf(index_expr)
stmts.append(index_stmts)
let index_ty = self.typekind_to_knf(index_expr.ty)
let index_name = self.expr_to_knf_name(index_knf_expr, index_ty, stmts)
(stmts, ArrayAccess(array_name, index_name))
}
FieldAccess(struct_expr, field_name) => {
let stmts = []
let (struct_stmts, struct_knf_expr) = self.left_value_to_knf(struct_expr)
stmts.append(struct_stmts)
let struct_ty = self.type_to_knf(struct_expr.ty)
let struct_name = self.expr_to_knf_name(struct_knf_expr, struct_ty, stmts)
(stmts, FieldAccess(struct_name, field_name))
}
}
}

51
src/knf/block.mbt Normal file
View File

@@ -0,0 +1,51 @@
///|
pub(all) struct KnfBlock {
stmts : Array[KnfStmt]
ty : Type
}
///|
pub fn Context::block_expr_to_knf(
self : Context,
expr : @typecheck.BlockExpr,
) -> KnfBlock raise KnfTransformError {
let stmts = []
for stmt in expr.stmts {
stmts.append(self.stmt_to_knf(stmt))
}
if stmts is [.., ExprStmt(Unit)] {
ignore(stmts.pop())
}
let ty = self.typekind_to_knf(expr.ty)
{ stmts, ty }
}
///|
pub fn KnfBlock::to_string(self : KnfBlock, ident : Int) -> String {
let sb = StringBuilder::new()
sb.write_string("{\n")
for stmt in self.stmts {
sb.write_string(stmt.to_string(ident=ident + 2))
sb.write_string("\n")
}
sb.write_string(" ".repeat(ident))
sb.write_string("}")
sb.to_string()
}
///|
pub fn KnfBlock::nested_to_string(self : KnfBlock) -> String {
let sb = StringBuilder::new()
sb.write_string("{")
for stmt in self.stmts {
sb.write_string(stmt.to_string(ident=0))
sb.write_string(" ")
}
sb.write_string("}")
sb.to_string()
}
///|
pub impl Show for KnfBlock with output(self, logger) {
logger.write_string(self.to_string(0))
}

83
src/knf/closure.mbt Normal file
View File

@@ -0,0 +1,83 @@
///|
pub(all) struct KnfClosure {
name : Name
params : Array[(Name, Type)]
ret_ty : Type
body : KnfBlock
captured_vars : Map[Name, Type]
}
///|
pub fn Context::local_function_to_knf(
self : Context,
local_function : @typecheck.LocalFunction,
) -> KnfClosure raise KnfTransformError {
let { fname, param_list, ret_ty, body } = local_function
// 1. 函数类型构造
let knf_params = []
let param_types = []
for param in param_list {
let (param_name, param_ty) = param
let knf_param_ty = self.type_to_knf(param_ty)
knf_params.push((param_name, knf_param_ty))
param_types.push(knf_param_ty)
}
let knf_ret_ty = self.type_to_knf(ret_ty)
let func_type = Function(param_types, knf_ret_ty)
let knf_func_name = self.add_new_name(fname, func_type)
// 2. 进入函数作用域
self.enter_scope()
// 3. 参数处理和函数体转换
let knf_param_names = []
for param in knf_params {
let (param_name, param_ty) = param
let knf_param_name = self.add_new_name(param_name, param_ty)
knf_param_names.push((knf_param_name, param_ty))
}
let knf_body = self.block_expr_to_knf(body)
// 捕获的变量
let captured_vars = Map::new()
for name in self.name_env.capture.keys() {
let ty = self.name_env.capture.get(name).unwrap()
captured_vars.set(name, ty)
}
// 4. 退出作用域并构造闭包
self.exit_scope()
{
name: knf_func_name,
params: knf_param_names,
ret_ty: knf_ret_ty,
body: knf_body,
captured_vars,
}
}
///|
pub fn KnfClosure::to_string(self : KnfClosure, ident? : Int = 0) -> String {
let sb = StringBuilder::new()
let indent_str = " ".repeat(ident)
if !self.captured_vars.is_empty() {
sb.write_string("// Captured variables: \n")
for name, ty in self.captured_vars {
sb.write_string(indent_str)
sb.write_string("// - \{name} : \{ty}\n")
}
sb.write_string(indent_str)
sb.write_string("fn \{self.name}(")
} else {
sb.write_string("fn \{self.name}(")
}
let param_strs = self.params.map(name_ty => {
let (name, ty) = name_ty
"\{name} : \{ty}"
})
sb.write_string(param_strs.join(", "))
sb.write_string(") -> \{self.ret_ty} ")
sb.write_string(self.body.to_string(ident))
sb.to_string()
}

134
src/knf/context.mbt Normal file
View File

@@ -0,0 +1,134 @@
///|
pub(all) suberror KnfTransformError String derive(Show)
///|
pub(all) struct Name {
id : String
slot : Int
} derive(Hash, Eq)
///|
pub fn Name::wildcard() -> Name {
Name::{ id: "_", slot: 0 }
}
///|
pub impl Show for Name with output(self, logger) {
logger.write_string(self.id)
if self.slot > 0 {
logger.write_string("$\{self.slot}")
}
}
///|
pub(all) struct Env {
local_ : Map[String, (Name, Type)] // defined in this scope
capture : Map[Name, Type] // captured from outer scopes
parent : Env?
}
///|
pub fn Env::new(parent? : Env? = None) -> Env {
Env::{ local_: Map::new(), capture: Map::new(), parent }
}
///|
pub fn Env::get_name_type(self : Env, name : Name) -> Type? {
let { id, .. } = name
match self.local_.get(id) {
Some((_, t)) => Some(t)
None =>
match self.parent {
Some(p) => p.get_name_type(name)
None => None
}
}
}
///|
pub fn Env::get(self : Env, name : String, no_capture? : Bool = false) -> Name? {
match self.local_.get(name) {
Some((n, _)) => Some(n)
None =>
match self.parent {
Some(p) =>
match p.get(name, no_capture~) {
Some(n) => {
if !no_capture {
let ty = p.get_name_type(n).unwrap()
self.capture.set(n, ty)
}
Some(n)
}
None => None
}
None => None
}
}
}
///|
pub fn Env::set(self : Env, s : String, name : Name, ty : Type) -> Unit {
self.local_.set(s, (name, ty))
}
///|
pub(all) struct Context {
mut name_env : Env
capture : Array[Name]
globals : Map[String, Type]
}
///|
pub fn Context::new() -> Context {
Context::{ name_env: Env::new(), capture: Array::new(), globals: Map::new() }
}
///|
pub fn Context::lookup_name(self : Context, s : String) -> (Name, Type)? {
let local_ = self.name_env.get(s)
if local_ is Some(name) {
return Some((name, self.name_env.get_name_type(name).unwrap()))
}
let global = self.globals.get(s)
if global is Some(ty) {
return Some(({ id: s, slot: 0 }, ty))
}
None
}
///|
pub fn Context::enter_scope(self : Context) -> Unit {
let sub_env = Env::new(parent=Some(self.name_env))
self.name_env = sub_env
}
///|
pub fn Context::exit_scope(self : Context) -> Unit {
self.name_env = match self.name_env.parent {
Some(p) => p
None => self.name_env
}
}
///|
pub fn Context::add_new_name(self : Context, s : String, ty : Type) -> Name {
match self.name_env.get(s, no_capture=true) {
Some({ id, slot }) => {
let name = Name::{ id, slot: slot + 1 }
self.name_env.set(s, name, ty)
name
}
None => {
let name = Name::{ id: s, slot: 0 }
self.name_env.set(s, name, ty)
name
}
}
}
///|
pub fn Context::add_temp(self : Context, ty : Type) -> Name {
let temp_id = "tmp"
self.add_new_name(temp_id, ty)
}

228
src/knf/expr.mbt Normal file
View File

@@ -0,0 +1,228 @@
///|
pub(all) enum KnfExpr {
Unit
Int(Int)
Bool(Bool)
Double(Double)
Ident(Name)
Not(Name)
Neg(Name)
Binary(BinaryOp, Name, Name)
If(KnfExpr, KnfBlock, KnfBlock)
Block(KnfBlock)
Call(Name, Array[Name])
ArrayAccess(Name, Name)
FieldAccess(Name, String)
TupleAccess(Name, Int)
CreateStruct(String, Array[(String, Name)])
ArrayLiteral(Type, Array[Name])
ArrayMake(Name, Name)
TupleLiteral(Array[Name])
}
///|
pub(all) enum BinaryOp {
Add // +
Sub // -
Mul // *
Div // /
Mod // %
Eq // ==
NE // !=
LT // <
GT // >
LE // <=
GE // >=
And // &&
Or // ||
} derive(Eq)
///|
pub fn Context::expr_to_knf(
self : Context,
expr : @typecheck.Expr,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
match expr.kind {
ApplyExpr(apply_expr) => self.apply_expr_to_knf(apply_expr)
NotExpr(inner) | NegExpr(inner) => {
let stmts = []
let (inner_stmts, inner_expr) = self.expr_to_knf(inner)
stmts.append(inner_stmts)
let ty = self.typekind_to_knf(inner.ty)
let tmp_name = self.add_temp(ty)
stmts.push(Let(tmp_name, ty, inner_expr))
let knf_expr = match expr.kind {
NotExpr(_) => Not(tmp_name)
NegExpr(_) => Neg(tmp_name)
_ => panic()
}
(stmts, knf_expr)
}
Compare(op, lhs, rhs) => {
let op = match op {
Equal => Eq
NotEqual => NE
Less => LT
Greater => GT
LessEqual => LE
GreaterEqual => GE
}
self.binary_expr_to_knf(op, lhs, rhs)
}
AddSub(op, lhs, rhs) => {
let op = match op {
Add => Add
Sub => Sub
}
self.binary_expr_to_knf(op, lhs, rhs)
}
MulDivRem(op, lhs, rhs) => {
let op = match op {
Mul => Mul
Div => Div
Rem => Mod
}
self.binary_expr_to_knf(op, lhs, rhs)
}
Or(lhs, rhs) => self.binary_expr_to_knf(Or, lhs, rhs)
And(lhs, rhs) => self.binary_expr_to_knf(And, lhs, rhs)
BlockExpr(block_expr) => {
let knf_block = self.block_expr_to_knf(block_expr)
([], Block(knf_block))
}
IfExpr(if_expr) => self.if_expr_to_knf(if_expr)
}
}
///|
pub fn Context::expr_to_knf_name(
self : Context,
expr : KnfExpr,
ty : Type,
stmts : Array[KnfStmt],
) -> Name {
match expr {
Ident(name) => name
_ => {
let tmp_name = self.add_temp(ty)
stmts.push(Let(tmp_name, ty, expr))
tmp_name
}
}
}
///|
pub fn Context::binary_expr_to_knf(
self : Context,
op : BinaryOp,
lhs : @typecheck.Expr,
rhs : @typecheck.Expr,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
let stmts = []
let (lhs_stmts, lhs_expr) = self.expr_to_knf(lhs)
let (rhs_stmts, rhs_expr) = self.expr_to_knf(rhs)
stmts.append(lhs_stmts)
stmts.append(rhs_stmts)
let ty = self.typekind_to_knf(lhs.ty)
let lhs_name = self.expr_to_knf_name(lhs_expr, ty, stmts)
let rhs_name = self.expr_to_knf_name(rhs_expr, ty, stmts)
let knf_expr = Binary(op, lhs_name, rhs_name)
(stmts, knf_expr)
}
///|
pub fn Context::if_expr_to_knf(
self : Context,
if_expr : @typecheck.IfExpr,
) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError {
let stmts = []
let (cond_stmts, cond_knf_expr) = self.expr_to_knf(if_expr.cond)
stmts.append(cond_stmts)
let then_block = self.block_expr_to_knf(if_expr.then_block)
let else_block = match if_expr.else_block {
None => { stmts: [], ty: Unit }
Some(expr) => {
let (else_stmts, nested_if_knf) = self.expr_to_knf(expr)
stmts.append(else_stmts)
match nested_if_knf {
Block(knf_block) => knf_block
_ =>
{
stmts: [ExprStmt(nested_if_knf)],
ty: self.typekind_to_knf(expr.ty),
}
}
}
}
(stmts, If(cond_knf_expr, then_block, else_block))
}
///|
pub impl Show for BinaryOp with output(self, logger) {
let s = match self {
Add => "+"
Sub => "-"
Mul => "*"
Div => "/"
Mod => "%"
Eq => "=="
NE => "!="
LT => "<"
GT => ">"
LE => "<="
GE => ">="
And => "&&"
Or => "||"
}
logger.write_string(s)
}
///|
pub fn KnfExpr::to_string(self : KnfExpr, ident? : Int = 0) -> String {
match self {
Unit => "()"
Int(i) => i.to_string()
Bool(b) => b.to_string()
Double(d) => d.to_string()
Ident(name) => name.to_string()
Not(name) => "!\{name}"
Neg(name) => "-\{name}"
Binary(op, lhs, rhs) => "\{lhs} \{op} \{rhs}"
Call(func_name, args) => {
let args_strs = args.map(arg => arg.to_string()).join(", ")
"\{func_name}(\{args_strs})"
}
ArrayAccess(array_name, index_name) => "\{array_name}[\{index_name}]"
FieldAccess(struct_name, field_name) => "\{struct_name}.\{field_name}"
TupleAccess(tuple_name, index) => "\{tuple_name}.\{index}"
CreateStruct(struct_name, init_arr) => {
let init_strs = init_arr.map(field => "\{field.0}: \{field.1}").join(", ")
"\{struct_name}::{\{init_strs}}"
}
ArrayLiteral(ty, elem_names) => {
let elems_strs = elem_names.map(elem => elem.to_string()).join(", ")
"[\{elems_strs}]::Array[\{ty}]"
}
ArrayMake(size_name, init_name) => "array_make(\{size_name}, \{init_name})"
TupleLiteral(elem_names) => {
let elems_strs = elem_names.map(elem => elem.to_string()).join(", ")
"(\{elems_strs})"
}
Block(block) => block.to_string(ident)
If(cond, then_block, else_block) => {
let cond_str = cond.to_string()
let then_str : String = then_block.to_string(ident)
if else_block.stmts.is_empty() {
"if \{cond_str} \{then_str}"
} else {
let else_str : String = else_block.to_string(ident)
"if \{cond_str} \{then_str} else \{else_str}"
}
}
}
}
///|
pub impl Show for KnfExpr with output(self, logger) {
logger.write_string(self.to_string(ident=0))
}

62
src/knf/function.mbt Normal file
View File

@@ -0,0 +1,62 @@
///|
pub(all) struct KnfFunction {
name : String
ret_ty : Type
params : Array[(Name, Type)]
body : KnfBlock
}
///|
pub fn Context::top_function_to_knf(
self : Context,
top_func : @typecheck.TopFunction,
) -> KnfFunction raise KnfTransformError {
let { fname, param_list, ty, body } = top_func
let func_type = self.typekind_to_knf(ty)
self.globals.set(fname, func_type)
// 1. 进入函数作用域
self.enter_scope()
// 2. 参数处理
let knf_params = []
let param_types = []
for param in param_list {
let { name: param_name, ty: param_ty } = param
let knf_param_ty = self.typekind_to_knf(param_ty)
let knf_param_name = self.add_new_name(param_name, knf_param_ty)
knf_params.push((knf_param_name, knf_param_ty))
param_types.push(knf_param_ty)
}
// 3. 返回类型转换
guard func_type is Function(_, ret_ty) else {
raise KnfTransformError("Function type expected")
}
// 4. 函数体转换
let knf_body = self.block_expr_to_knf(body)
// 5. 退出作用域
self.exit_scope()
{ name: fname, ret_ty, params: knf_params, body: knf_body }
}
///|
pub impl Show for KnfFunction with output(self, logger) {
let { name, ret_ty, params, body } = self
logger.write_string("fn \{name}")
if name != "main" {
logger.write_string("(")
let param_str = params
.map(param => {
let (param_name, param_ty) = param
"\{param_name}: \{param_ty}"
})
.join(", ")
logger.write_string(param_str)
logger.write_string(") -> \{ret_ty}")
}
logger.write_char(' ')
logger.write_object(body)
}

85
src/knf/knf.mbt Normal file
View File

@@ -0,0 +1,85 @@
///|
pub(all) struct KnfProgram {
struct_defs : Map[String, KnfStructDef]
top_lets : Map[String, KnfTopLet]
functions : Map[String, KnfFunction]
}
///|
pub fn Context::program_to_knf(
self : Context,
prog : @typecheck.Program,
) -> KnfProgram raise KnfTransformError {
let knf_struct_defs = Map::new()
for name, struct_def in prog.struct_defs {
let knf_struct_def = self.struct_def_to_knf(struct_def)
knf_struct_defs.set(name, knf_struct_def)
}
for name, func in prog.top_functions {
let func_type = self.typekind_to_knf(func.ty)
self.globals.set(name, func_type)
}
let knf_top_lets = Map::new()
for name, top_let in prog.top_lets {
let knf_top_let = self.top_let_to_knf(top_let)
knf_top_lets.set(name, knf_top_let)
}
let knf_functions = Map::new()
for name, func in prog.top_functions {
let knf_func = self.top_function_to_knf(func)
knf_functions.set(name, knf_func)
}
{
struct_defs: knf_struct_defs,
top_lets: knf_top_lets,
functions: knf_functions,
}
}
///|
pub fn knf_transform(
prog : @typecheck.Program,
) -> KnfProgram raise KnfTransformError {
let context = Context::new()
context.add_intrinsic_functions()
context.program_to_knf(prog)
}
///|
pub fn Context::add_intrinsic_functions(self : Context) -> Unit {
let map = Map::of([
("read_int", Function([], Int)),
("print_int", Function([Int], Unit)),
("read_char", Function([], Int)),
("print_char", Function([Int], Unit)),
("print_endline", Function([], Unit)),
("int_of_float", Function([Double], Int)),
("float_of_int", Function([Int], Double)),
("truncate", Function([Double], Int)),
("floor", Function([Double], Double)),
("abs_float", Function([Double], Double)),
("sqrt", Function([Double], Double)),
("sin", Function([Double], Double)),
("cos", Function([Double], Double)),
("atan", Function([Double], Double)),
])
for name, ty in map {
self.globals.set(name, ty)
}
}
///|
pub impl Show for KnfProgram with output(self, logger) {
for _, struct_def in self.struct_defs {
logger.write_object(struct_def)
logger.write_char('\n')
}
for _, top_let in self.top_lets {
logger.write_object(top_let)
logger.write_char('\n')
}
for _, func in self.functions {
logger.write_object(func)
logger.write_char('\n')
}
}

1417
src/knf/knf_test.mbt Normal file

File diff suppressed because it is too large Load Diff

75
src/knf/let_stmt.mbt Normal file
View File

@@ -0,0 +1,75 @@
///|
pub fn Context::let_mut_stmt_to_knf(
self : Context,
let_mut_stmt : @typecheck.LetMutStmt,
) -> Array[KnfStmt] raise KnfTransformError {
let { name, ty, expr } = let_mut_stmt
let stmts = []
let (init_stmts, init_knf_expr) = self.expr_to_knf(expr)
stmts.append(init_stmts)
let ty = self.type_to_knf(ty)
let name = self.add_new_name(name, ty)
stmts.push(LetMut(name, ty, init_knf_expr))
stmts
}
///|
pub fn Context::let_stmt_to_knf(
self : Context,
let_stmt : @typecheck.LetStmt,
) -> Array[KnfStmt] raise KnfTransformError {
let { pattern, ty, expr } = let_stmt
let stmts = []
let (init_stmts, init_knf_expr) = self.expr_to_knf(expr)
stmts.append(init_stmts)
let ty = self.typekind_to_knf(ty)
match pattern.kind {
Ident(name) => {
let name = self.add_new_name(name, ty)
stmts.push(Let(name, ty, init_knf_expr))
stmts
}
Wildcard => {
let name = Name::wildcard()
stmts.push(Let(name, ty, init_knf_expr))
stmts
}
Tuple(names) => {
guard ty is Tuple(types) else {
raise KnfTransformError("tuple pattern requires tuple type")
}
if init_knf_expr is TupleLiteral(elem_names) {
guard names.length() == elem_names.length() else {
raise KnfTransformError("tuple pattern length mismatch")
}
for i in 0..<names.length() {
let elem_name = match names[i].kind {
Ident(s) => self.add_new_name(s, types[i])
Wildcard => Name::wildcard()
Tuple(_) => panic()
}
let elem_ty = types[i]
let value_expr = KnfExpr::Ident(elem_names[i])
stmts.push(Let(elem_name, elem_ty, value_expr))
}
} else {
// Handle non-tuple expression case
let tmp_name = self.add_temp(ty)
for i in 0..<names.length() {
let elem_name = match names[i].kind {
Ident(s) => self.add_new_name(s, types[i])
Wildcard => Name::wildcard()
Tuple(_) => panic()
}
let elem_ty = types[i]
let value_expr = KnfExpr::ArrayAccess(
tmp_name,
self.expr_to_knf_name(Int(i), Int, stmts),
)
stmts.push(Let(elem_name, elem_ty, value_expr))
}
}
stmts
}
}
}

8
src/knf/moon.pkg.json Normal file
View File

@@ -0,0 +1,8 @@
{
"import": [
"Lil-Ran/lilunar/typecheck"
],
"test-import": [
"Lil-Ran/lilunar/parser"
]
}

99
src/knf/stmt.mbt Normal file
View File

@@ -0,0 +1,99 @@
///|
pub(all) enum KnfStmt {
Let(Name, Type, KnfExpr) // let a : Int = 42;
LetMut(Name, Type, KnfExpr) // let mut a : Int = 0;
Assign(Name, KnfExpr) // a = 10;
ArrayPut(Name, Name, KnfExpr) // arr[3] = 5;
StructFieldSet(Name, String, Name) // point.x = 10; // MiniMoonBit does not support
While(KnfBlock, KnfBlock) // while (cond) { ... }
ExprStmt(KnfExpr) // expr;
Return(KnfExpr) // return expr;
ReturnUnit // return;
ClosureDef(KnfClosure) // closure definition
}
///|
pub fn Context::stmt_to_knf(
self : Context,
stmt : @typecheck.Stmt,
) -> Array[KnfStmt] raise KnfTransformError {
match stmt.kind {
LetStmt(let_stmt) => self.let_stmt_to_knf(let_stmt)
LetMutStmt(let_mut_stmt) => self.let_mut_stmt_to_knf(let_mut_stmt)
AssignStmt(assign_stmt) => self.assign_stmt_to_knf(assign_stmt)
WhileStmt(while_stmt) => self.while_stmt_to_knf(while_stmt)
ExprStmt(expr_stmt) => {
let stmts = []
let (expr_stmts, expr_knf_expr) = self.expr_to_knf(expr_stmt)
stmts.append(expr_stmts)
stmts.push(ExprStmt(expr_knf_expr))
stmts
}
ReturnStmt(return_stmt) =>
match return_stmt.ty {
Unit => [ReturnUnit]
_ => {
let stmts = []
let (expr_stmts, expr_knf_expr) = self.expr_to_knf(return_stmt)
stmts.append(expr_stmts)
stmts.push(Return(expr_knf_expr))
stmts
}
}
LocalFunction(local_function) => {
let closure = self.local_function_to_knf(local_function)
[ClosureDef(closure)]
}
}
}
///|
pub fn Context::while_stmt_to_knf(
self : Context,
while_stmt : @typecheck.WhileStmt,
) -> Array[KnfStmt] raise KnfTransformError {
let (cond_stmts, cond_knf_expr) = self.expr_to_knf(while_stmt.cond)
[
While(
{ stmts: [..cond_stmts, ExprStmt(cond_knf_expr)], ty: Bool },
self.block_expr_to_knf(while_stmt.body),
),
]
}
///|
pub fn KnfStmt::to_string(self : KnfStmt, ident? : Int = 0) -> String {
let s = match self {
Let(name, ty, expr) => "let \{name} : \{ty} = \{expr};"
LetMut(name, ty, expr) => "let mut \{name} : \{ty} = \{expr};"
Assign(name, expr) => "\{name} = \{expr};"
ArrayPut(array_name, index_name, value_expr) =>
"\{array_name}[\{index_name}] = \{value_expr};"
StructFieldSet(struct_name, field_name, value_name) =>
"\{struct_name}.\{field_name} = \{value_name};"
While(cond_block, body_block) =>
if cond_block.stmts.length() <= 3 {
let cond_str = cond_block.nested_to_string()
let body_str = body_block.to_string(ident)
"while \{cond_str} \{body_str}"
} else {
let cond_str = cond_block.to_string(ident)
let body_str = body_block.to_string(ident)
"while \{cond_str} \{body_str}"
}
ExprStmt(expr) => {
let expr_str = expr.to_string(ident~)
"\{expr_str};"
}
Return(expr) => "return \{expr};"
ReturnUnit => "return;"
ClosureDef(closure) => closure.to_string(ident~)
}
let indent_str = " ".repeat(ident)
"\{indent_str}\{s}"
}
///|
pub impl Show for KnfStmt with output(self, logger) {
logger.write_string(self.to_string(ident=0))
}

47
src/knf/struct_def.mbt Normal file
View File

@@ -0,0 +1,47 @@
///|
pub(all) struct KnfStructDef {
name : String
// field name, is_mut, field type
fields : Array[(String, Bool, Type)]
}
///|
pub fn Context::struct_def_to_knf(
self : Context,
struct_def : @typecheck.StructDef,
) -> KnfStructDef raise KnfTransformError {
let { name, fields } = struct_def
let knf_fields = []
for field in fields {
let { name: field_name, ty } = field
let field_type = self.typekind_to_knf(ty.kind)
knf_fields.push((field_name, ty.mutable, field_type))
}
{ name, fields: knf_fields }
}
///|
pub fn KnfStructDef::get_field_index(
self : KnfStructDef,
field_name : String,
) -> Int? {
for i, f in self.fields {
let (name, _, _) = f
if name == field_name {
return Some(i)
}
}
None
}
///|
pub impl Show for KnfStructDef with output(self, logger) {
let { name, fields } = self
logger.write_string("struct \{name} {\n")
for field in fields {
let (field_name, is_mut, field_type) = field
let mutability = if is_mut { "mut " } else { "" }
logger.write_string(" \{mutability}\{field_name}: \{field_type};\n")
}
logger.write_string("}\n")
}

29
src/knf/top_let.mbt Normal file
View File

@@ -0,0 +1,29 @@
///|
pub(all) struct KnfTopLet {
name : Name
ty : Type
expr : KnfExpr
init_stmts : Array[KnfStmt]
}
///|
pub fn Context::top_let_to_knf(
self : Context,
top_let : @typecheck.TopLet,
) -> KnfTopLet raise KnfTransformError {
let { name, ty, expr } = top_let
let (init_stmts, expr) = self.expr_to_knf(expr)
let ty = self.type_to_knf(ty)
let name = self.add_new_name(name, ty)
self.globals.set(top_let.name, ty)
{ name, ty, expr, init_stmts }
}
///|
pub impl Show for KnfTopLet with output(self, logger) {
let { name, ty, expr, init_stmts } = self
for stmt in init_stmts {
logger.write_string(" \{stmt};")
}
logger.write_string("let \{name} : \{ty} = \{expr};")
}

64
src/knf/type.mbt Normal file
View File

@@ -0,0 +1,64 @@
///|
pub(all) enum Type {
Unit
Int
Bool
Double
Array(Type)
Struct(String)
Tuple(Array[Type])
Function(Array[Type], Type)
}
///|
pub impl Show for Type with output(self, logger) {
let s = match self {
Unit => "Unit"
Int => "Int"
Bool => "Bool"
Double => "Double"
Array(elem_type) => "Array[\{elem_type}]"
Struct(name) => "\{name}"
Tuple(elem_types) => {
let elem_strs = elem_types.map(et => "\{et}").join(", ")
"(\{elem_strs})"
}
Function(param_types, ret_type) => {
let param_strs = param_types.map(pt => "\{pt}").join(", ")
"(\{param_strs}) -> \{ret_type}"
}
}
logger.write_string(s)
}
///|
pub fn Context::typekind_to_knf(
self : Context,
tk : @typecheck.TypeKind,
) -> Type raise KnfTransformError {
match tk {
Any => raise KnfTransformError("Cannot convert 'Any' to KNF type.")
Struct(name) => Struct(name)
Function(param_types, ret_type) =>
Function(
param_types.map(pt => self.typekind_to_knf(pt)),
self.typekind_to_knf(ret_type),
)
Array(t) => Array(self.typekind_to_knf(t))
Tuple(types) => Tuple(types.map(t => self.typekind_to_knf(t)))
Double => Double
Int => Int
Bool => Bool
Unit => Unit
TypeVar(_) =>
raise KnfTransformError("Cannot convert 'TypeVar' to KNF type.")
}
}
///|
pub fn Context::type_to_knf(
self : Context,
t : @typecheck.Type,
) -> Type raise KnfTransformError {
self.typekind_to_knf(t.kind)
}

View File

@@ -81,6 +81,14 @@ pub fn Context::check_atom_expr(
let field_exprs = [] let field_exprs = []
for field in fields { for field in fields {
let (field_name, field_expr) = field let (field_name, field_expr) = field
for existing_field in field_exprs {
let (existing_field_name, _) = existing_field
if existing_field_name == field_name {
raise TypeCheckError(
"Duplicate field '\{field_name}' in struct '\{name}' construction.",
)
}
}
let expected_type = def let expected_type = def
.get_field_type(field_name) .get_field_type(field_name)
.or_error( .or_error(

View File

@@ -15,9 +15,16 @@ pub fn Context::check_block_expr(
self.enter_scope() self.enter_scope()
let checked_stmts = stmts.map(stmt => self.check_stmt(stmt)) let checked_stmts = stmts.map(stmt => self.check_stmt(stmt))
self.exit_scope() self.exit_scope()
let stmts_count = checked_stmts.length() guard checked_stmts is [.., { kind: ExprStmt(expr) }] else {
guard stmts_count > 0 && checked_stmts[stmts_count - 1].kind is ExprStmt(expr) else {
return { stmts: checked_stmts, ty: Unit } return { stmts: checked_stmts, ty: Unit }
} }
if checked_stmts is [.., { kind: ReturnStmt(ret) }] {
{ stmts: checked_stmts, ty: ret.ty }
} else if checked_stmts is [.., stmt1, stmt2] &&
stmt2.kind is ExprStmt({ ty: Unit, .. }) &&
stmt1.kind is ReturnStmt(ret) {
{ stmts: checked_stmts, ty: ret.ty }
} else {
{ stmts: checked_stmts, ty: expr.ty } { stmts: checked_stmts, ty: expr.ty }
}
} }

View File

@@ -8,7 +8,7 @@ pub(all) struct Param {
pub(all) struct TopFunction { pub(all) struct TopFunction {
fname : String fname : String
param_list : Array[Param] param_list : Array[Param]
ret_ty : TypeKind ty : TypeKind
body : BlockExpr body : BlockExpr
} derive(Show) } derive(Show)
@@ -28,7 +28,8 @@ pub fn Context::check_top_function_body(
} }
let param_names = func.params.map(param => param.0) let param_names = func.params.map(param => param.0)
let param_list = [] let param_list = []
guard self.func_types.get(func.id) is Some(Function(param_types, ret_ty)) else { guard self.func_types.get(func.id) is Some(ty) &&
ty is Function(param_types, ret_ty) else {
raise TypeCheckError("Function type for '\{func.id}' not found.") raise TypeCheckError("Function type for '\{func.id}' not found.")
} }
self.enter_scope() self.enter_scope()
@@ -48,7 +49,7 @@ pub fn Context::check_top_function_body(
if func.user_defined_type is Some(UserDefined(udt)) { if func.user_defined_type is Some(UserDefined(udt)) {
self.type_env.local_.remove("$Generic$\{udt}") self.type_env.local_.remove("$Generic$\{udt}")
} }
{ fname: func.id, param_list, ret_ty, body: checked_body } { fname: func.id, param_list, ty, body: checked_body }
} }
///| ///|

View File

@@ -33,7 +33,7 @@ pub fn Context::substitute_type_var(
name: param.name, name: param.name,
ty: self.deref_type_var(param.ty), ty: self.deref_type_var(param.ty),
}), }),
ret_ty: self.deref_type_var(top_func.ret_ty), ty: self.deref_type_var(top_func.ty),
body: self.substitute_type_var_for_block_expr(top_func.body), body: self.substitute_type_var_for_block_expr(top_func.body),
}) })
} }