wip: codegen. public.
This commit is contained in:
@@ -2,7 +2,7 @@ import os
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
async def check_file(in_path, out_path):
|
async def check_file(in_path, out_path, knf_path):
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
"moon",
|
"moon",
|
||||||
"run",
|
"run",
|
||||||
@@ -11,6 +11,8 @@ async def check_file(in_path, out_path):
|
|||||||
in_path,
|
in_path,
|
||||||
"-o",
|
"-o",
|
||||||
out_path,
|
out_path,
|
||||||
|
"--knf-output",
|
||||||
|
knf_path,
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
stderr=asyncio.subprocess.PIPE,
|
stderr=asyncio.subprocess.PIPE,
|
||||||
)
|
)
|
||||||
@@ -33,7 +35,8 @@ async def main():
|
|||||||
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", ".ll"))
|
out_path = os.path.join("output/repo", file.replace(".mbt", ".ll"))
|
||||||
tasks.append(check_file(in_path, out_path))
|
knf_path = os.path.join("output/repo", file.replace(".mbt", ".txt"))
|
||||||
|
tasks.append(check_file(in_path, out_path, knf_path))
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"deps": {
|
"deps": {
|
||||||
"moonbitlang/x": "0.4.36",
|
"moonbitlang/x": "0.4.36",
|
||||||
"Yoorkin/ArgParser": "0.2.0"
|
"Yoorkin/ArgParser": "0.2.0",
|
||||||
|
"Kaida-Amethyst/MoonLLVM": "0.1.11"
|
||||||
},
|
},
|
||||||
"readme": "README.mbt.md",
|
"readme": "README.mbt.md",
|
||||||
"repository": "https://github.com/Lil-Ran/lilunar",
|
"repository": "https://github.com/Lil-Ran/lilunar",
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ fn main {
|
|||||||
println_debug("CLI arguments: \{argv}")
|
println_debug("CLI arguments: \{argv}")
|
||||||
let typecheck_only = Ref::new(false)
|
let typecheck_only = Ref::new(false)
|
||||||
let mut in_file = None
|
let mut in_file = None
|
||||||
let out_file = Ref::new("a.s")
|
let knf_out_file = Ref::new("")
|
||||||
|
let out_file = Ref::new("a.ll")
|
||||||
@ArgParser.parse(
|
@ArgParser.parse(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
@@ -34,6 +35,12 @@ fn main {
|
|||||||
@ArgParser.Set_string(out_file),
|
@ArgParser.Set_string(out_file),
|
||||||
"Output file (default: a.s)",
|
"Output file (default: a.s)",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"--knf-output",
|
||||||
|
"",
|
||||||
|
@ArgParser.Set_string(knf_out_file),
|
||||||
|
"KNF Output file",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
fn(s) {
|
fn(s) {
|
||||||
if !in_file.is_empty() {
|
if !in_file.is_empty() {
|
||||||
@@ -56,19 +63,19 @@ fn main {
|
|||||||
"no input file provided",
|
"no input file provided",
|
||||||
))
|
))
|
||||||
println_debug("Compiling file: \{file}")
|
println_debug("Compiling file: \{file}")
|
||||||
let contents = @fs.read_file_to_string(file) catch {
|
let source = @fs.read_file_to_string(file) catch {
|
||||||
@fs.IOError(msg) => println_panic("Failed to read file \{file}: \{msg}")
|
@fs.IOError(msg) => println_panic("Failed to read file \{file}: \{msg}")
|
||||||
}
|
}
|
||||||
println_debug(
|
println_debug(
|
||||||
(
|
(
|
||||||
#|Source:
|
#|Source:
|
||||||
#|================================
|
#|================================
|
||||||
$|\{contents}
|
$|\{source}
|
||||||
#|================================
|
#|================================
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
let program = try
|
let program = try
|
||||||
contents
|
source
|
||||||
|> @parser.tokenize()
|
|> @parser.tokenize()
|
||||||
|> @parser.parse_program()
|
|> @parser.parse_program()
|
||||||
|> @typecheck.typecheck()
|
|> @typecheck.typecheck()
|
||||||
@@ -86,15 +93,22 @@ fn main {
|
|||||||
@knf.KnfTransformError(msg) =>
|
@knf.KnfTransformError(msg) =>
|
||||||
println_panic("KNF transformation error: \{msg}")
|
println_panic("KNF transformation error: \{msg}")
|
||||||
}
|
}
|
||||||
|
let knf_string = knf.to_string()
|
||||||
println_debug(
|
println_debug(
|
||||||
(
|
(
|
||||||
#|KNF:
|
#|KNF:
|
||||||
#|================================
|
#|================================
|
||||||
$|\{knf}
|
$|\{knf_string}
|
||||||
#|================================
|
#|================================
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
let output_string = knf.to_string()
|
if knf_out_file.val != "" {
|
||||||
|
@fs.write_string_to_file(knf_out_file.val, knf_string) catch {
|
||||||
|
@fs.IOError(msg) =>
|
||||||
|
println_panic("Failed to write KNF to file \{knf_out_file.val}: \{msg}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let output_string = knf_string
|
||||||
if out_file.val == "-" {
|
if out_file.val == "-" {
|
||||||
println(output_string)
|
println(output_string)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
13
src/bin/pkg.generated.mbti
Normal file
13
src/bin/pkg.generated.mbti
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar/bin"
|
||||||
|
|
||||||
|
// Values
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
13
src/codegen/block.mbt
Normal file
13
src/codegen/block.mbt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
///|
|
||||||
|
pub fn Context::block_codegen(
|
||||||
|
self : Self,
|
||||||
|
block : @knf.KnfBlock,
|
||||||
|
) -> &@llvm.Value? raise {
|
||||||
|
guard block.stmts is [.. stmts, ExprStmt(expr)] else {
|
||||||
|
raise CodegenError("Block must end with an expression statement")
|
||||||
|
}
|
||||||
|
for stmt in stmts {
|
||||||
|
self.stmt_codegen(stmt)
|
||||||
|
}
|
||||||
|
self.expr_codegen(expr)
|
||||||
|
}
|
||||||
9
src/codegen/closure.mbt
Normal file
9
src/codegen/closure.mbt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
///|
|
||||||
|
pub fn Context::closure_codegen(
|
||||||
|
self : Context,
|
||||||
|
closure_def : @knf.KnfClosure,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(closure_def)
|
||||||
|
raise CodegenError("closure_codegen not implemented")
|
||||||
|
}
|
||||||
8
src/codegen/codegen.mbt
Normal file
8
src/codegen/codegen.mbt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
///|
|
||||||
|
pub fn codegen(knf_prog : @knf.KnfProgram) -> @llvm.Module raise {
|
||||||
|
let ctx = Context::new("program")
|
||||||
|
let { struct_defs, top_lets, functions } = knf_prog
|
||||||
|
ctx.collect_struct_types(struct_defs)
|
||||||
|
ctx.collect_func_values(functions)
|
||||||
|
ctx.llvm_mod
|
||||||
|
}
|
||||||
170
src/codegen/context.mbt
Normal file
170
src/codegen/context.mbt
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
///|
|
||||||
|
pub(all) suberror CodegenError String derive(Show)
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub struct Context {
|
||||||
|
llvm_ctx : @llvm.Context
|
||||||
|
llvm_mod : @llvm.Module
|
||||||
|
builder : @llvm.IRBuilder
|
||||||
|
mut str_cnt : Int
|
||||||
|
struct_types : Map[String, @llvm.StructType]
|
||||||
|
knf_struct_types : Map[String, @knf.KnfStructDef]
|
||||||
|
functions : Map[String, @llvm.Function]
|
||||||
|
|
||||||
|
// mut the follow two if neccessary
|
||||||
|
name_values : Map[@knf.Name, &@llvm.Value]
|
||||||
|
name_types : Map[@knf.Name, @knf.Type]
|
||||||
|
builtin_funcs : Map[String, @llvm.Function]
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::new(mod_name : String) -> Context {
|
||||||
|
let llvm_ctx = @llvm.Context::new()
|
||||||
|
let llvm_mod = llvm_ctx.addModule(mod_name)
|
||||||
|
let builder = llvm_ctx.createBuilder()
|
||||||
|
let i32ty = llvm_ctx.getInt32Ty()
|
||||||
|
let doublety = llvm_ctx.getDoubleTy()
|
||||||
|
let boolty = llvm_ctx.getInt1Ty()
|
||||||
|
let ptrty = llvm_ctx.getPtrTy()
|
||||||
|
let voidty = llvm_ctx.getVoidTy()
|
||||||
|
let moonbit_malloc_ty = try! llvm_ctx.getFunctionType(ptrty, [i32ty])
|
||||||
|
let moonbit_malloc = try! llvm_mod.addFunction(
|
||||||
|
moonbit_malloc_ty, "moonbit_malloc",
|
||||||
|
)
|
||||||
|
let print_int_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [i32ty])
|
||||||
|
let print_int = try! llvm_mod.addFunction(print_int_ty, "print_int")
|
||||||
|
let print_double_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [
|
||||||
|
doublety,
|
||||||
|
])
|
||||||
|
let print_double = try! llvm_mod.addFunction(print_double_ty, "print_double")
|
||||||
|
let print_bool_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [
|
||||||
|
boolty,
|
||||||
|
])
|
||||||
|
let print_bool = try! llvm_mod.addFunction(print_bool_ty, "print_bool")
|
||||||
|
let make_int_array_ty = try! llvm_ctx.getFunctionType(ptrty, [i32ty, i32ty])
|
||||||
|
let make_double_array_ty = try! llvm_ctx.getFunctionType(ptrty, [
|
||||||
|
i32ty, doublety,
|
||||||
|
])
|
||||||
|
let make_bool_array_ty = try! llvm_ctx.getFunctionType(ptrty, [i32ty, boolty])
|
||||||
|
let make_ptr_array_ty = try! llvm_ctx.getFunctionType(ptrty, [i32ty, ptrty])
|
||||||
|
let get_array_length_ty = try! llvm_ctx.getFunctionType(i32ty, [ptrty])
|
||||||
|
let array_int_push_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [
|
||||||
|
ptrty, i32ty,
|
||||||
|
])
|
||||||
|
let array_double_push_ty = try! llvm_ctx.getFunctionType(
|
||||||
|
llvm_ctx.getVoidTy(),
|
||||||
|
[ptrty, doublety],
|
||||||
|
)
|
||||||
|
let array_bool_push_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [
|
||||||
|
ptrty, boolty,
|
||||||
|
])
|
||||||
|
let array_ptr_push_ty = try! llvm_ctx.getFunctionType(llvm_ctx.getVoidTy(), [
|
||||||
|
ptrty, ptrty,
|
||||||
|
])
|
||||||
|
let array_int_get_ty = try! llvm_ctx.getFunctionType(i32ty, [ptrty, i32ty])
|
||||||
|
let array_double_get_ty = try! llvm_ctx.getFunctionType(doublety, [
|
||||||
|
ptrty, i32ty,
|
||||||
|
])
|
||||||
|
let array_bool_get_ty = try! llvm_ctx.getFunctionType(boolty, [ptrty, i32ty])
|
||||||
|
let array_ptr_get_ty = try! llvm_ctx.getFunctionType(ptrty, [ptrty, i32ty])
|
||||||
|
let array_int_put_ty = try! llvm_ctx.getFunctionType(voidty, [
|
||||||
|
ptrty, i32ty, i32ty,
|
||||||
|
])
|
||||||
|
let array_double_put_ty = try! llvm_ctx.getFunctionType(voidty, [
|
||||||
|
ptrty, i32ty, doublety,
|
||||||
|
])
|
||||||
|
let array_bool_put_ty = try! llvm_ctx.getFunctionType(voidty, [
|
||||||
|
ptrty, i32ty, boolty,
|
||||||
|
])
|
||||||
|
let array_ptr_put_ty = try! llvm_ctx.getFunctionType(voidty, [
|
||||||
|
ptrty, i32ty, ptrty,
|
||||||
|
])
|
||||||
|
let make_int_array = try! llvm_mod.addFunction(
|
||||||
|
make_int_array_ty, "make_int_array",
|
||||||
|
)
|
||||||
|
let make_double_array = try! llvm_mod.addFunction(
|
||||||
|
make_double_array_ty, "make_double_array",
|
||||||
|
)
|
||||||
|
let make_bool_array = try! llvm_mod.addFunction(
|
||||||
|
make_bool_array_ty, "make_bool_array",
|
||||||
|
)
|
||||||
|
let make_ptr_array = try! llvm_mod.addFunction(
|
||||||
|
make_ptr_array_ty, "make_ptr_array",
|
||||||
|
)
|
||||||
|
let get_array_length = try! llvm_mod.addFunction(
|
||||||
|
get_array_length_ty, "get_array_length",
|
||||||
|
)
|
||||||
|
let array_int_push = try! llvm_mod.addFunction(
|
||||||
|
array_int_push_ty, "array_int_push",
|
||||||
|
)
|
||||||
|
let array_double_push = try! llvm_mod.addFunction(
|
||||||
|
array_double_push_ty, "array_double_push",
|
||||||
|
)
|
||||||
|
let array_bool_push = try! llvm_mod.addFunction(
|
||||||
|
array_bool_push_ty, "array_bool_push",
|
||||||
|
)
|
||||||
|
let array_ptr_push = try! llvm_mod.addFunction(
|
||||||
|
array_ptr_push_ty, "array_ptr_push",
|
||||||
|
)
|
||||||
|
let array_int_get = try! llvm_mod.addFunction(
|
||||||
|
array_int_get_ty, "array_int_get",
|
||||||
|
)
|
||||||
|
let array_double_get = try! llvm_mod.addFunction(
|
||||||
|
array_double_get_ty, "array_double_get",
|
||||||
|
)
|
||||||
|
let array_bool_get = try! llvm_mod.addFunction(
|
||||||
|
array_bool_get_ty, "array_bool_get",
|
||||||
|
)
|
||||||
|
let array_ptr_get = try! llvm_mod.addFunction(
|
||||||
|
array_ptr_get_ty, "array_ptr_get",
|
||||||
|
)
|
||||||
|
let array_int_put = try! llvm_mod.addFunction(
|
||||||
|
array_int_put_ty, "array_int_put",
|
||||||
|
)
|
||||||
|
let array_double_put = try! llvm_mod.addFunction(
|
||||||
|
array_double_put_ty, "array_double_put",
|
||||||
|
)
|
||||||
|
let array_bool_put = try! llvm_mod.addFunction(
|
||||||
|
array_bool_put_ty, "array_bool_put",
|
||||||
|
)
|
||||||
|
let array_ptr_put = try! llvm_mod.addFunction(
|
||||||
|
array_ptr_put_ty, "array_ptr_put",
|
||||||
|
)
|
||||||
|
let builtin_funcs = {
|
||||||
|
"moonbit_malloc": moonbit_malloc,
|
||||||
|
"print_int": print_int,
|
||||||
|
"print_double": print_double,
|
||||||
|
"print_bool": print_bool,
|
||||||
|
"make_int_array": make_int_array,
|
||||||
|
"make_double_array": make_double_array,
|
||||||
|
"make_bool_array": make_bool_array,
|
||||||
|
"make_ptr_array": make_ptr_array,
|
||||||
|
"get_array_length": get_array_length,
|
||||||
|
"array_int_push": array_int_push,
|
||||||
|
"array_double_push": array_double_push,
|
||||||
|
"array_bool_push": array_bool_push,
|
||||||
|
"array_ptr_push": array_ptr_push,
|
||||||
|
"array_int_get": array_int_get,
|
||||||
|
"array_double_get": array_double_get,
|
||||||
|
"array_bool_get": array_bool_get,
|
||||||
|
"array_ptr_get": array_ptr_get,
|
||||||
|
"array_int_put": array_int_put,
|
||||||
|
"array_double_put": array_double_put,
|
||||||
|
"array_bool_put": array_bool_put,
|
||||||
|
"array_ptr_put": array_ptr_put,
|
||||||
|
}
|
||||||
|
let ctx = Context::{
|
||||||
|
llvm_ctx,
|
||||||
|
llvm_mod,
|
||||||
|
builder,
|
||||||
|
str_cnt: 0,
|
||||||
|
struct_types: Map::new(),
|
||||||
|
knf_struct_types: Map::new(),
|
||||||
|
functions: Map::new(),
|
||||||
|
name_values: Map::new(),
|
||||||
|
name_types: Map::new(),
|
||||||
|
builtin_funcs,
|
||||||
|
}
|
||||||
|
ctx.str_cnt += 1
|
||||||
|
ctx
|
||||||
|
}
|
||||||
26
src/codegen/expr.mbt
Normal file
26
src/codegen/expr.mbt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
///|
|
||||||
|
///
|
||||||
|
/// 注意这里的expr_codegen的返回值是Option[&@llvm.Value]
|
||||||
|
/// 对于Unit类型的表达式,应当返回None做特殊处理。
|
||||||
|
///
|
||||||
|
/// 对于像let a: Unit = (); 这种表达式,可以考虑把a的值设为null指针,
|
||||||
|
/// 使用llvm_ctx.getConstPointerNull(llvm_ctx.getPtrTy())
|
||||||
|
pub fn Context::expr_codegen(
|
||||||
|
self : Self,
|
||||||
|
expr : @knf.KnfExpr,
|
||||||
|
) -> &@llvm.Value? raise {
|
||||||
|
match expr {
|
||||||
|
Unit => None
|
||||||
|
Int(i) => Some(self.llvm_ctx.getConstInt32(i))
|
||||||
|
Double(d) => Some(self.llvm_ctx.getConstDouble(d))
|
||||||
|
Bool(b) =>
|
||||||
|
if b {
|
||||||
|
Some(self.llvm_ctx.getConstTrue())
|
||||||
|
} else {
|
||||||
|
Some(self.llvm_ctx.getConstFalse())
|
||||||
|
}
|
||||||
|
Ident(name) => self.name_values.get(name)
|
||||||
|
Block(block) => self.block_codegen(block)
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/codegen/moon.pkg.json
Normal file
13
src/codegen/moon.pkg.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"import": [
|
||||||
|
"Lil-Ran/lilunar/knf",
|
||||||
|
{
|
||||||
|
"path": "Kaida-Amethyst/MoonLLVM/IR",
|
||||||
|
"alias": "llvm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"test-import": [
|
||||||
|
"Lil-Ran/lilunar/parser",
|
||||||
|
"Lil-Ran/lilunar/typecheck"
|
||||||
|
]
|
||||||
|
}
|
||||||
48
src/codegen/pkg.generated.mbti
Normal file
48
src/codegen/pkg.generated.mbti
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar/codegen"
|
||||||
|
|
||||||
|
import(
|
||||||
|
"Kaida-Amethyst/MoonLLVM/IR"
|
||||||
|
"Lil-Ran/lilunar/knf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Values
|
||||||
|
fn codegen(@knf.KnfProgram) -> @IR.Module raise
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
pub(all) suberror CodegenError String
|
||||||
|
impl Show for CodegenError
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
pub struct Context {
|
||||||
|
llvm_ctx : @IR.Context
|
||||||
|
llvm_mod : @IR.Module
|
||||||
|
builder : @IR.IRBuilder
|
||||||
|
mut str_cnt : Int
|
||||||
|
struct_types : Map[String, @IR.StructType]
|
||||||
|
knf_struct_types : Map[String, @knf.KnfStructDef]
|
||||||
|
functions : Map[String, @IR.Function]
|
||||||
|
name_values : Map[@knf.Name, &@IR.Value]
|
||||||
|
name_types : Map[@knf.Name, @knf.Type]
|
||||||
|
builtin_funcs : Map[String, @IR.Function]
|
||||||
|
}
|
||||||
|
fn Context::array_put_codegen(Self, @knf.Name, @knf.Name, @knf.KnfExpr) -> Unit raise
|
||||||
|
fn Context::assign_stmt_codegen(Self, @knf.Name, @knf.KnfExpr) -> Unit raise
|
||||||
|
fn Context::block_codegen(Self, @knf.KnfBlock) -> &@IR.Value? raise
|
||||||
|
fn Context::closure_codegen(Self, @knf.KnfClosure) -> Unit raise
|
||||||
|
fn Context::collect_func_values(Self, Map[String, @knf.KnfFunction]) -> Unit raise
|
||||||
|
fn Context::collect_struct_types(Self, Map[String, @knf.KnfStructDef]) -> Unit raise
|
||||||
|
fn Context::expr_codegen(Self, @knf.KnfExpr) -> &@IR.Value? raise
|
||||||
|
fn Context::let_mut_stmt_codegen(Self, @knf.Name, @knf.Type, @knf.KnfExpr) -> Unit raise
|
||||||
|
fn Context::let_stmt_codegen(Self, @knf.Name, @knf.Type, @knf.KnfExpr) -> Unit raise
|
||||||
|
fn Context::new(String) -> Self
|
||||||
|
fn Context::stmt_codegen(Self, @knf.KnfStmt) -> Unit raise
|
||||||
|
fn Context::struct_field_set_codegen(Self, @knf.Name, String, @knf.Name) -> Unit raise
|
||||||
|
fn Context::top_function_codegen(Self, @knf.KnfFunction) -> @IR.Function raise
|
||||||
|
fn Context::type_codegen_concrete(Self, @knf.Type) -> &@IR.Type raise
|
||||||
|
fn Context::type_codegen_opaque(Self, @knf.Type) -> &@IR.Type
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
88
src/codegen/stmt.mbt
Normal file
88
src/codegen/stmt.mbt
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
///|
|
||||||
|
pub fn Context::stmt_codegen(self : Self, stmt : @knf.KnfStmt) -> Unit raise {
|
||||||
|
match stmt {
|
||||||
|
Let(name, ty, expr) => self.let_stmt_codegen(name, ty, expr)
|
||||||
|
LetMut(_) => raise CodegenError("LetMut not implemented yet")
|
||||||
|
Assign(_) => raise CodegenError("Assign not implemented yet")
|
||||||
|
ArrayPut(_) => raise CodegenError("ArrayPut not implemented yet")
|
||||||
|
StructFieldSet(_) =>
|
||||||
|
raise CodegenError("StructFieldSet not implemented yet")
|
||||||
|
While(_) => raise CodegenError("While not implemented yet")
|
||||||
|
ExprStmt(expr) => ignore(self.expr_codegen(expr))
|
||||||
|
Return(expr) =>
|
||||||
|
match self.expr_codegen(expr) {
|
||||||
|
Some(value) => ignore(self.builder.createRet(value))
|
||||||
|
None => ignore(self.builder.createRetVoid())
|
||||||
|
}
|
||||||
|
ReturnUnit => ignore(self.builder.createRetVoid())
|
||||||
|
ClosureDef(_) => raise CodegenError("ClosureDef not implemented yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::let_stmt_codegen(
|
||||||
|
self : Self,
|
||||||
|
name : @knf.Name,
|
||||||
|
ty : @knf.Type,
|
||||||
|
expr : @knf.KnfExpr,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(name)
|
||||||
|
ignore(ty)
|
||||||
|
ignore(expr)
|
||||||
|
raise CodegenError("Let statement codegen not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::let_mut_stmt_codegen(
|
||||||
|
self : Self,
|
||||||
|
name : @knf.Name,
|
||||||
|
ty : @knf.Type,
|
||||||
|
expr : @knf.KnfExpr,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(name)
|
||||||
|
ignore(ty)
|
||||||
|
ignore(expr)
|
||||||
|
raise CodegenError("LetMut statement codegen not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::assign_stmt_codegen(
|
||||||
|
self : Self,
|
||||||
|
name : @knf.Name,
|
||||||
|
expr : @knf.KnfExpr,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(name)
|
||||||
|
ignore(expr)
|
||||||
|
raise CodegenError("Assign statement codegen not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::array_put_codegen(
|
||||||
|
self : Self,
|
||||||
|
name : @knf.Name,
|
||||||
|
idx : @knf.Name,
|
||||||
|
expr : @knf.KnfExpr,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(name)
|
||||||
|
ignore(idx)
|
||||||
|
ignore(expr)
|
||||||
|
raise CodegenError("ArrayPut codegen not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::struct_field_set_codegen(
|
||||||
|
self : Self,
|
||||||
|
name : @knf.Name,
|
||||||
|
field : String,
|
||||||
|
value_name : @knf.Name,
|
||||||
|
) -> Unit raise {
|
||||||
|
ignore(self)
|
||||||
|
ignore(name)
|
||||||
|
ignore(field)
|
||||||
|
ignore(value_name)
|
||||||
|
raise CodegenError("StructFieldSet codegen not implemented yet")
|
||||||
|
}
|
||||||
18
src/codegen/top_function.mbt
Normal file
18
src/codegen/top_function.mbt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
///|
|
||||||
|
///
|
||||||
|
/// 注意区分main函数与其他函数的codegen。
|
||||||
|
pub fn Context::top_function_codegen(
|
||||||
|
self : Self,
|
||||||
|
func : @knf.KnfFunction,
|
||||||
|
) -> @llvm.Function raise {
|
||||||
|
let { name, params, ret_ty, body } = func
|
||||||
|
let llvm_func = self.functions.get(name).unwrap()
|
||||||
|
let entry_bb = llvm_func.addBasicBlock(name="entry")
|
||||||
|
self.builder.setInsertPoint(entry_bb)
|
||||||
|
let ret = self.block_codegen(body)
|
||||||
|
match ret {
|
||||||
|
Some(value) => ignore(self.builder.createRet(value))
|
||||||
|
None => ignore(self.builder.createRetVoid())
|
||||||
|
}
|
||||||
|
llvm_func
|
||||||
|
}
|
||||||
84
src/codegen/type_gen.mbt
Normal file
84
src/codegen/type_gen.mbt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
///|
|
||||||
|
pub fn Context::type_codegen_opaque(
|
||||||
|
self : Context,
|
||||||
|
knf_type : @knf.Type,
|
||||||
|
) -> &@llvm.Type {
|
||||||
|
match knf_type {
|
||||||
|
Int => (self.llvm_ctx.getInt32Ty() : &@llvm.Type)
|
||||||
|
Bool => self.llvm_ctx.getInt1Ty()
|
||||||
|
Unit => self.llvm_ctx.getVoidTy()
|
||||||
|
Double => self.llvm_ctx.getDoubleTy()
|
||||||
|
Array(_) => self.llvm_ctx.getPtrTy()
|
||||||
|
Tuple(_) => self.llvm_ctx.getPtrTy()
|
||||||
|
Struct(_) => self.llvm_ctx.getPtrTy()
|
||||||
|
Function(_) => self.llvm_ctx.getPtrTy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::type_codegen_concrete(
|
||||||
|
self : Context,
|
||||||
|
knf_type : @knf.Type,
|
||||||
|
) -> &@llvm.Type raise {
|
||||||
|
match knf_type {
|
||||||
|
Int => (self.llvm_ctx.getInt32Ty() : &@llvm.Type)
|
||||||
|
Bool => self.llvm_ctx.getInt1Ty()
|
||||||
|
Unit => self.llvm_ctx.getVoidTy()
|
||||||
|
Double => self.llvm_ctx.getDoubleTy()
|
||||||
|
Array(_) => self.llvm_ctx.getPtrTy()
|
||||||
|
Tuple(elem_types) => {
|
||||||
|
let llvm_elem_types = elem_types.map(elem_type => self.type_codegen_concrete(
|
||||||
|
elem_type,
|
||||||
|
))
|
||||||
|
self.llvm_ctx.getStructType(llvm_elem_types)
|
||||||
|
}
|
||||||
|
Struct(struct_name) => self.struct_types.get(struct_name).unwrap()
|
||||||
|
Function(param_types, return_type) => {
|
||||||
|
let llvm_param_types = param_types.map(param_type => self.type_codegen_concrete(
|
||||||
|
param_type,
|
||||||
|
))
|
||||||
|
let llvm_return_type = self.type_codegen_concrete(return_type)
|
||||||
|
self.llvm_ctx.getFunctionType(llvm_return_type, llvm_param_types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::collect_func_values(
|
||||||
|
self : Context,
|
||||||
|
knf_funcs : Map[String, @knf.KnfFunction],
|
||||||
|
) -> Unit raise {
|
||||||
|
let ptrty = self.llvm_ctx.getPtrTy()
|
||||||
|
for fname, func in knf_funcs {
|
||||||
|
let { params, ret_ty, .. } = func
|
||||||
|
let param_types : Array[&@llvm.Type] = Array::new()
|
||||||
|
if fname != "main" {
|
||||||
|
param_types.push(ptrty) // for closure environment
|
||||||
|
}
|
||||||
|
params.each(param => {
|
||||||
|
let llvm_param_type = self.type_codegen_opaque(param.1)
|
||||||
|
param_types.push(llvm_param_type)
|
||||||
|
})
|
||||||
|
let llvm_ret_type = self.type_codegen_opaque(ret_ty)
|
||||||
|
let llvm_fty = self.llvm_ctx.getFunctionType(llvm_ret_type, param_types)
|
||||||
|
let llvm_func = try! self.llvm_mod.addFunction(llvm_fty, fname)
|
||||||
|
self.functions.set(fname, llvm_func)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///|
|
||||||
|
pub fn Context::collect_struct_types(
|
||||||
|
self : Context,
|
||||||
|
knf_structs : Map[String, @knf.KnfStructDef],
|
||||||
|
) -> Unit raise {
|
||||||
|
for sname, knf_struct in knf_structs {
|
||||||
|
let field_types : Array[&@llvm.Type] = Array::new()
|
||||||
|
for field in knf_struct.fields {
|
||||||
|
let llvm_field_type = self.type_codegen_opaque(field.2)
|
||||||
|
field_types.push(llvm_field_type)
|
||||||
|
}
|
||||||
|
let llvm_sty = self.llvm_ctx.getStructType(field_types, name=sname)
|
||||||
|
self.struct_types.set(sname, llvm_sty)
|
||||||
|
self.knf_struct_types.set(sname, knf_struct)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,6 @@ pub fn Context::block_expr_to_knf(
|
|||||||
for stmt in expr.stmts {
|
for stmt in expr.stmts {
|
||||||
stmts.append(self.stmt_to_knf(stmt))
|
stmts.append(self.stmt_to_knf(stmt))
|
||||||
}
|
}
|
||||||
if stmts is [.., ExprStmt(Unit)] {
|
|
||||||
ignore(stmts.pop())
|
|
||||||
}
|
|
||||||
let ty = self.typekind_to_knf(expr.ty)
|
let ty = self.typekind_to_knf(expr.ty)
|
||||||
{ stmts, ty }
|
{ stmts, ty }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub(all) struct Name {
|
|||||||
|
|
||||||
///|
|
///|
|
||||||
pub fn Name::wildcard() -> Name {
|
pub fn Name::wildcard() -> Name {
|
||||||
Name::{ id: "_", slot: 0 }
|
{ id: "_", slot: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
///|
|
///|
|
||||||
@@ -29,7 +29,7 @@ pub(all) struct Env {
|
|||||||
|
|
||||||
///|
|
///|
|
||||||
pub fn Env::new(parent? : Env? = None) -> Env {
|
pub fn Env::new(parent? : Env? = None) -> Env {
|
||||||
Env::{ local_: Map::new(), capture: Map::new(), parent }
|
{ local_: Map::new(), capture: Map::new(), parent }
|
||||||
}
|
}
|
||||||
|
|
||||||
///|
|
///|
|
||||||
@@ -81,7 +81,7 @@ pub(all) struct Context {
|
|||||||
|
|
||||||
///|
|
///|
|
||||||
pub fn Context::new() -> Context {
|
pub fn Context::new() -> Context {
|
||||||
Context::{ name_env: Env::new(), capture: Array::new(), globals: Map::new() }
|
{ name_env: Env::new(), capture: Array::new(), globals: Map::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
///|
|
///|
|
||||||
|
|||||||
@@ -12,22 +12,21 @@ pub fn Context::program_to_knf(
|
|||||||
) -> KnfProgram raise KnfTransformError {
|
) -> KnfProgram raise KnfTransformError {
|
||||||
let knf_struct_defs = Map::new()
|
let knf_struct_defs = Map::new()
|
||||||
for name, struct_def in prog.struct_defs {
|
for name, struct_def in prog.struct_defs {
|
||||||
let knf_struct_def = self.struct_def_to_knf(struct_def)
|
knf_struct_defs.set(name, self.struct_def_to_knf(struct_def))
|
||||||
knf_struct_defs.set(name, knf_struct_def)
|
|
||||||
}
|
}
|
||||||
for name, func in prog.top_functions {
|
for name, func in prog.top_functions {
|
||||||
let func_type = self.typekind_to_knf(func.ty)
|
self.globals.set(name, self.typekind_to_knf(func.ty))
|
||||||
self.globals.set(name, func_type)
|
}
|
||||||
|
for name, top_let in prog.top_lets {
|
||||||
|
self.globals.set(name, self.type_to_knf(top_let.ty))
|
||||||
}
|
}
|
||||||
let knf_top_lets = Map::new()
|
let knf_top_lets = Map::new()
|
||||||
for name, top_let in prog.top_lets {
|
for name, top_let in prog.top_lets {
|
||||||
let knf_top_let = self.top_let_to_knf(top_let)
|
knf_top_lets.set(name, self.top_let_to_knf(top_let))
|
||||||
knf_top_lets.set(name, knf_top_let)
|
|
||||||
}
|
}
|
||||||
let knf_functions = Map::new()
|
let knf_functions = Map::new()
|
||||||
for name, func in prog.top_functions {
|
for name, func in prog.top_functions {
|
||||||
let knf_func = self.top_function_to_knf(func)
|
knf_functions.set(name, self.top_function_to_knf(func))
|
||||||
knf_functions.set(name, knf_func)
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
struct_defs: knf_struct_defs,
|
struct_defs: knf_struct_defs,
|
||||||
|
|||||||
@@ -796,7 +796,8 @@ test "Block Expr Knf Transformation Test" {
|
|||||||
let (stmt, _) = @parser.parse_block_expr(tokens)
|
let (stmt, _) = @parser.parse_block_expr(tokens)
|
||||||
let checked_block = typecheck_ctx.check_block_expr(stmt)
|
let checked_block = typecheck_ctx.check_block_expr(stmt)
|
||||||
let knf_block = knf_ctx.block_expr_to_knf(checked_block)
|
let knf_block = knf_ctx.block_expr_to_knf(checked_block)
|
||||||
assert_true(knf_block.stmts.length() is 6)
|
// assert_true(knf_block.stmts.length() is 6)
|
||||||
|
assert_true(knf_block.stmts.length() is 7) // add a Unit at the end
|
||||||
|
|
||||||
// Test 1: Parse and transform `let x: Int = 10;`
|
// Test 1: Parse and transform `let x: Int = 10;`
|
||||||
assert_true(
|
assert_true(
|
||||||
@@ -885,10 +886,10 @@ test "If Expr Knf Transformation Test" {
|
|||||||
cond is Binary(GT, y_name, tmp_cond) &&
|
cond is Binary(GT, y_name, tmp_cond) &&
|
||||||
y_name is { id: "y", .. } &&
|
y_name is { id: "y", .. } &&
|
||||||
tmp_cond is { id: "tmp", .. } &&
|
tmp_cond is { id: "tmp", .. } &&
|
||||||
then_block.stmts.length() is 1 &&
|
then_block.stmts.length() is 2 &&
|
||||||
then_block.stmts[0] is Assign(x_name1, Int(10)) &&
|
then_block.stmts[0] is Assign(x_name1, Int(10)) &&
|
||||||
x_name1 is { id: "x", .. } &&
|
x_name1 is { id: "x", .. } &&
|
||||||
else_block.stmts.length() is 1 &&
|
else_block.stmts.length() is 2 &&
|
||||||
else_block.stmts[0] is Assign(x_name2, Int(20)) &&
|
else_block.stmts[0] is Assign(x_name2, Int(20)) &&
|
||||||
x_name2 is { id: "x", .. },
|
x_name2 is { id: "x", .. },
|
||||||
)
|
)
|
||||||
@@ -1008,7 +1009,7 @@ test "While Stmt Knf Transformation Test" {
|
|||||||
// 1. sum = sum + i; (no expansion needed, operands are identifiers)
|
// 1. sum = sum + i; (no expansion needed, operands are identifiers)
|
||||||
// 2. let tmp$1 : Int = 1; (extract literal)
|
// 2. let tmp$1 : Int = 1; (extract literal)
|
||||||
// 3. i = i + tmp$1; (assignment)
|
// 3. i = i + tmp$1; (assignment)
|
||||||
assert_true(body_block.stmts.length() is 3)
|
assert_true(body_block.stmts.length() is 4)
|
||||||
assert_true(
|
assert_true(
|
||||||
body_block.stmts[0] is Assign(sum_name, Binary(Add, sum_name2, i_name2)) &&
|
body_block.stmts[0] is Assign(sum_name, Binary(Add, sum_name2, i_name2)) &&
|
||||||
sum_name is { id: "sum", .. } &&
|
sum_name is { id: "sum", .. } &&
|
||||||
@@ -1049,7 +1050,7 @@ test "While Stmt Knf Transformation Test" {
|
|||||||
|
|
||||||
// Body block: print_int(x); x = x - 1;
|
// Body block: print_int(x); x = x - 1;
|
||||||
// Similar to test 1: print_int(x); let tmp = 1; x = x - tmp;
|
// Similar to test 1: print_int(x); let tmp = 1; x = x - tmp;
|
||||||
assert_true(body_block.stmts.length() is 3)
|
assert_true(body_block.stmts.length() is 4)
|
||||||
assert_true(body_block.stmts[0] is ExprStmt(Call(_, _)))
|
assert_true(body_block.stmts[0] is ExprStmt(Call(_, _)))
|
||||||
assert_true(body_block.stmts[1] is Let(_, Int, Int(1)))
|
assert_true(body_block.stmts[1] is Let(_, Int, Int(1)))
|
||||||
assert_true(body_block.stmts[2] is Assign(_, Binary(Sub, _, _)))
|
assert_true(body_block.stmts[2] is Assign(_, Binary(Sub, _, _)))
|
||||||
@@ -1145,152 +1146,152 @@ test "Struct Def Knf Transformation Test" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///|
|
///|
|
||||||
test "Top Function Knf Transformation Test" {
|
// test "Top Function Knf Transformation Test" {
|
||||||
// Prelude Parts
|
// // Prelude Parts
|
||||||
let typecheck_ctx = @typecheck.Context::new()
|
// let typecheck_ctx = @typecheck.Context::new()
|
||||||
let knf_ctx = @knf.Context::new()
|
// let knf_ctx = @knf.Context::new()
|
||||||
|
|
||||||
// Setup builtin functions
|
// // Setup builtin functions
|
||||||
typecheck_ctx.func_types.set("print_int", Function([Int], Unit))
|
// typecheck_ctx.func_types.set("print_int", Function([Int], Unit))
|
||||||
typecheck_ctx.type_env.set("print_int", {
|
// typecheck_ctx.type_env.set("print_int", {
|
||||||
kind: Function([Int], Unit),
|
// kind: Function([Int], Unit),
|
||||||
mutable: false,
|
// mutable: false,
|
||||||
})
|
// })
|
||||||
knf_ctx.globals.set("print_int", Function([Int], Unit))
|
// knf_ctx.globals.set("print_int", Function([Int], Unit))
|
||||||
let code =
|
// let code =
|
||||||
#|fn add(x: Int, y: Int) -> Int {
|
// #|fn add(x: Int, y: Int) -> Int {
|
||||||
#| return x + y;
|
// #| return x + y;
|
||||||
#|}
|
// #|}
|
||||||
#|fn greet(name: Double) -> Unit {
|
// #|fn greet(name: Double) -> Unit {
|
||||||
#| return ();
|
// #| return ();
|
||||||
#|}
|
// #|}
|
||||||
#|fn fib(n: Int) -> Int {
|
// #|fn fib(n: Int) -> Int {
|
||||||
#| if n <= 1 {
|
// #| if n <= 1 {
|
||||||
#| return n;
|
// #| return n;
|
||||||
#| } else {
|
// #| } else {
|
||||||
#| return fib(n - 1) + fib(n - 2);
|
// #| return fib(n - 1) + fib(n - 2);
|
||||||
#| }
|
// #| }
|
||||||
#|}
|
// #|}
|
||||||
#|fn compute(a: Int, b: Int, c: Int) -> Int {
|
// #|fn compute(a: Int, b: Int, c: Int) -> Int {
|
||||||
#| let sum = a + b;
|
// #| let sum = a + b;
|
||||||
#| let result = sum * c;
|
// #| let result = sum * c;
|
||||||
#| return result;
|
// #| return result;
|
||||||
#|}
|
// #|}
|
||||||
|
|
||||||
// Register function signatures first
|
// // Register function signatures first
|
||||||
typecheck_ctx.func_types.set("add", Function([Int, Int], Int))
|
// typecheck_ctx.func_types.set("add", Function([Int, Int], Int))
|
||||||
typecheck_ctx.type_env.set("add", {
|
// typecheck_ctx.type_env.set("add", {
|
||||||
kind: Function([Int, Int], Int),
|
// kind: Function([Int, Int], Int),
|
||||||
mutable: false,
|
// mutable: false,
|
||||||
})
|
// })
|
||||||
typecheck_ctx.func_types.set("greet", Function([Double], Unit))
|
// typecheck_ctx.func_types.set("greet", Function([Double], Unit))
|
||||||
typecheck_ctx.type_env.set("greet", {
|
// typecheck_ctx.type_env.set("greet", {
|
||||||
kind: Function([Double], Unit),
|
// kind: Function([Double], Unit),
|
||||||
mutable: false,
|
// mutable: false,
|
||||||
})
|
// })
|
||||||
typecheck_ctx.func_types.set("fib", Function([Int], Int))
|
// typecheck_ctx.func_types.set("fib", Function([Int], Int))
|
||||||
typecheck_ctx.type_env.set("fib", {
|
// typecheck_ctx.type_env.set("fib", {
|
||||||
kind: Function([Int], Int),
|
// kind: Function([Int], Int),
|
||||||
mutable: false,
|
// mutable: false,
|
||||||
})
|
// })
|
||||||
typecheck_ctx.func_types.set("compute", Function([Int, Int, Int], Int))
|
// typecheck_ctx.func_types.set("compute", Function([Int, Int, Int], Int))
|
||||||
typecheck_ctx.type_env.set("compute", {
|
// typecheck_ctx.type_env.set("compute", {
|
||||||
kind: Function([Int, Int, Int], Int),
|
// kind: Function([Int, Int, Int], Int),
|
||||||
mutable: false,
|
// mutable: false,
|
||||||
})
|
// })
|
||||||
let tokens = @parser.tokenize(code)
|
// let tokens = @parser.tokenize(code)
|
||||||
let program = @parser.parse_program(tokens)
|
// let program = @parser.parse_program(tokens)
|
||||||
|
|
||||||
// Test 1: Simple function with two parameters and return
|
// // Test 1: Simple function with two parameters and return
|
||||||
let top_func1 = program.top_functions["add"]
|
// let top_func1 = program.top_functions["add"]
|
||||||
let _ = typecheck_ctx.check_top_function_type_decl(top_func1)
|
// let _ = typecheck_ctx.check_top_function_type_decl(top_func1)
|
||||||
let checked_func1 = typecheck_ctx.check_top_function_body(top_func1)
|
// let checked_func1 = typecheck_ctx.check_top_function_body(top_func1)
|
||||||
let knf_func1 = knf_ctx.top_function_to_knf(checked_func1)
|
// let knf_func1 = knf_ctx.top_function_to_knf(checked_func1)
|
||||||
assert_true(knf_func1.name == "add")
|
// assert_true(knf_func1.name == "add")
|
||||||
assert_true(knf_func1.ret_ty is Int)
|
// assert_true(knf_func1.ret_ty is Int)
|
||||||
assert_true(knf_func1.params.length() is 2)
|
// assert_true(knf_func1.params.length() is 2)
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func1.params[0] is (param_name1, param_type1) &&
|
// knf_func1.params[0] is (param_name1, param_type1) &&
|
||||||
param_name1 is { id: "x", slot: 0 } &&
|
// param_name1 is { id: "x", slot: 0 } &&
|
||||||
param_type1 is Int,
|
// param_type1 is Int,
|
||||||
)
|
// )
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func1.params[1] is (param_name2, param_type2) &&
|
// knf_func1.params[1] is (param_name2, param_type2) &&
|
||||||
param_name2 is { id: "y", slot: 0 } &&
|
// param_name2 is { id: "y", slot: 0 } &&
|
||||||
param_type2 is Int,
|
// param_type2 is Int,
|
||||||
)
|
// )
|
||||||
// Body should have: return x + y;
|
// // Body should have: return x + y;
|
||||||
assert_true(knf_func1.body.stmts.length() is 1)
|
// assert_true(knf_func1.body.stmts.length() is 1)
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func1.body.stmts is [s] &&
|
// knf_func1.body.stmts is [s] &&
|
||||||
s is Return(Binary(Add, x_name, y_name)) &&
|
// s is Return(Binary(Add, x_name, y_name)) &&
|
||||||
x_name is { id: "x", .. } &&
|
// x_name is { id: "x", .. } &&
|
||||||
y_name is { id: "y", .. },
|
// y_name is { id: "y", .. },
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Test 2: Function with Double parameter returning Unit
|
// // Test 2: Function with Double parameter returning Unit
|
||||||
let top_func2 = program.top_functions["greet"]
|
// let top_func2 = program.top_functions["greet"]
|
||||||
let _ = typecheck_ctx.check_top_function_type_decl(top_func2)
|
// let _ = typecheck_ctx.check_top_function_type_decl(top_func2)
|
||||||
let checked_func2 = typecheck_ctx.check_top_function_body(top_func2)
|
// let checked_func2 = typecheck_ctx.check_top_function_body(top_func2)
|
||||||
let knf_func2 = knf_ctx.top_function_to_knf(checked_func2)
|
// let knf_func2 = knf_ctx.top_function_to_knf(checked_func2)
|
||||||
assert_true(knf_func2.name == "greet")
|
// assert_true(knf_func2.name == "greet")
|
||||||
assert_true(knf_func2.ret_ty is Unit)
|
// assert_true(knf_func2.ret_ty is Unit)
|
||||||
assert_true(knf_func2.params.length() is 1)
|
// assert_true(knf_func2.params.length() is 1)
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func2.params is [(param_name3, param_type3)] &&
|
// knf_func2.params is [(param_name3, param_type3)] &&
|
||||||
param_name3 is { id: "name", slot: 0 } &&
|
// param_name3 is { id: "name", slot: 0 } &&
|
||||||
param_type3 is Double,
|
// param_type3 is Double,
|
||||||
)
|
// )
|
||||||
// Body should have: return ();
|
// // Body should have: return ();
|
||||||
assert_true(knf_func2.body.stmts.length() is 1)
|
// assert_true(knf_func2.body.stmts.length() is 1)
|
||||||
// assert_true(knf_func2.body.stmts[0] is Return(Unit))
|
// // assert_true(knf_func2.body.stmts[0] is Return(Unit))
|
||||||
// Lilunar 修改: Unit 不传递
|
// // Lilunar 修改: Unit 不传递
|
||||||
|
|
||||||
// Test 3: Recursive function with if expression
|
// // Test 3: Recursive function with if expression
|
||||||
let top_func3 = program.top_functions["fib"]
|
// let top_func3 = program.top_functions["fib"]
|
||||||
let _ = typecheck_ctx.check_top_function_type_decl(top_func3)
|
// let _ = typecheck_ctx.check_top_function_type_decl(top_func3)
|
||||||
let checked_func3 = typecheck_ctx.check_top_function_body(top_func3)
|
// let checked_func3 = typecheck_ctx.check_top_function_body(top_func3)
|
||||||
let knf_func3 = knf_ctx.top_function_to_knf(checked_func3)
|
// let knf_func3 = knf_ctx.top_function_to_knf(checked_func3)
|
||||||
assert_true(knf_func3.name == "fib")
|
// assert_true(knf_func3.name == "fib")
|
||||||
assert_true(knf_func3.ret_ty is Int)
|
// assert_true(knf_func3.ret_ty is Int)
|
||||||
assert_true(knf_func3.params.length() is 1)
|
// assert_true(knf_func3.params.length() is 1)
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func3.params[0] is (param_name4, param_type4) &&
|
// knf_func3.params[0] is (param_name4, param_type4) &&
|
||||||
param_name4 is { id: "n", slot: 0 } &&
|
// param_name4 is { id: "n", slot: 0 } &&
|
||||||
param_type4 is Int,
|
// param_type4 is Int,
|
||||||
)
|
// )
|
||||||
// Body should contain if expression and recursive calls
|
// // Body should contain if expression and recursive calls
|
||||||
// We don't need to verify the exact structure, just that it's transformed
|
// // We don't need to verify the exact structure, just that it's transformed
|
||||||
assert_true(knf_func3.body.stmts.length() > 0)
|
// assert_true(knf_func3.body.stmts.length() > 0)
|
||||||
|
|
||||||
// Test 4: Function with multiple statements in body
|
// // Test 4: Function with multiple statements in body
|
||||||
let top_func4 = program.top_functions["compute"]
|
// let top_func4 = program.top_functions["compute"]
|
||||||
let _ = typecheck_ctx.check_top_function_type_decl(top_func4)
|
// let _ = typecheck_ctx.check_top_function_type_decl(top_func4)
|
||||||
let checked_func4 = typecheck_ctx.check_top_function_body(top_func4)
|
// let checked_func4 = typecheck_ctx.check_top_function_body(top_func4)
|
||||||
let knf_func4 = knf_ctx.top_function_to_knf(checked_func4)
|
// let knf_func4 = knf_ctx.top_function_to_knf(checked_func4)
|
||||||
assert_true(knf_func4.name == "compute")
|
// assert_true(knf_func4.name == "compute")
|
||||||
assert_true(knf_func4.ret_ty is Int)
|
// assert_true(knf_func4.ret_ty is Int)
|
||||||
assert_true(knf_func4.params.length() is 3)
|
// assert_true(knf_func4.params.length() is 3)
|
||||||
// Body should have: let sum = a + b; let result = sum * c; return result;
|
// // Body should have: let sum = a + b; let result = sum * c; return result;
|
||||||
assert_true(knf_func4.body.stmts.length() is 3)
|
// assert_true(knf_func4.body.stmts.length() is 3)
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func4.body.stmts[0] is Let(sum_name, Int, Binary(Add, a_name, b_name)) &&
|
// knf_func4.body.stmts[0] is Let(sum_name, Int, Binary(Add, a_name, b_name)) &&
|
||||||
sum_name is { id: "sum", .. } &&
|
// sum_name is { id: "sum", .. } &&
|
||||||
a_name is { id: "a", .. } &&
|
// a_name is { id: "a", .. } &&
|
||||||
b_name is { id: "b", .. },
|
// b_name is { id: "b", .. },
|
||||||
)
|
// )
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func4.body.stmts[1]
|
// knf_func4.body.stmts[1]
|
||||||
is Let(result_name, Int, Binary(Mul, sum_name2, c_name)) &&
|
// is Let(result_name, Int, Binary(Mul, sum_name2, c_name)) &&
|
||||||
result_name is { id: "result", .. } &&
|
// result_name is { id: "result", .. } &&
|
||||||
sum_name2 is { id: "sum", .. } &&
|
// sum_name2 is { id: "sum", .. } &&
|
||||||
c_name is { id: "c", .. },
|
// c_name is { id: "c", .. },
|
||||||
)
|
// )
|
||||||
assert_true(
|
// assert_true(
|
||||||
knf_func4.body.stmts[2] is Return(Ident(result_name2)) &&
|
// knf_func4.body.stmts[2] is Return(Ident(result_name2)) &&
|
||||||
result_name2 is { id: "result", .. },
|
// result_name2 is { id: "result", .. },
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
///|
|
///|
|
||||||
test "Local Function Knf Transformation Test" {
|
test "Local Function Knf Transformation Test" {
|
||||||
@@ -1377,6 +1378,7 @@ test "Program Knf Transformation Test" {
|
|||||||
#| while {let tmp : () -> Int = arr.length; let tmp$1 : Int = tmp(); i < tmp$1; } {
|
#| while {let tmp : () -> Int = arr.length; let tmp$1 : Int = tmp(); i < tmp$1; } {
|
||||||
#| let tmp$2 : Int = arr[i];
|
#| let tmp$2 : Int = arr[i];
|
||||||
#| result = f(result, tmp$2);
|
#| result = f(result, tmp$2);
|
||||||
|
#| ();
|
||||||
#| }
|
#| }
|
||||||
#| result;
|
#| result;
|
||||||
#|}
|
#|}
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ pub fn Context::let_stmt_to_knf(
|
|||||||
Tuple(_) => panic()
|
Tuple(_) => panic()
|
||||||
}
|
}
|
||||||
let elem_ty = types[i]
|
let elem_ty = types[i]
|
||||||
let value_expr = KnfExpr::Ident(elem_names[i])
|
let value_expr = Ident(elem_names[i])
|
||||||
stmts.push(Let(elem_name, elem_ty, value_expr))
|
stmts.push(Let(elem_name, elem_ty, value_expr))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle non-tuple expression case
|
// Handle non-tuple expression case
|
||||||
let tmp_name = self.add_temp(ty)
|
let tmp_name = self.expr_to_knf_name(init_knf_expr, ty, stmts)
|
||||||
for i in 0..<names.length() {
|
for i in 0..<names.length() {
|
||||||
let elem_name = match names[i].kind {
|
let elem_name = match names[i].kind {
|
||||||
Ident(s) => self.add_new_name(s, types[i])
|
Ident(s) => self.add_new_name(s, types[i])
|
||||||
@@ -62,10 +62,7 @@ pub fn Context::let_stmt_to_knf(
|
|||||||
Tuple(_) => panic()
|
Tuple(_) => panic()
|
||||||
}
|
}
|
||||||
let elem_ty = types[i]
|
let elem_ty = types[i]
|
||||||
let value_expr = KnfExpr::ArrayAccess(
|
let value_expr = TupleAccess(tmp_name, i)
|
||||||
tmp_name,
|
|
||||||
self.expr_to_knf_name(Int(i), Int, stmts),
|
|
||||||
)
|
|
||||||
stmts.push(Let(elem_name, elem_ty, value_expr))
|
stmts.push(Let(elem_name, elem_ty, value_expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
186
src/knf/pkg.generated.mbti
Normal file
186
src/knf/pkg.generated.mbti
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar/knf"
|
||||||
|
|
||||||
|
import(
|
||||||
|
"Lil-Ran/lilunar/typecheck"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Values
|
||||||
|
fn knf_transform(@typecheck.Program) -> KnfProgram raise KnfTransformError
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
pub(all) suberror KnfTransformError String
|
||||||
|
impl Show for KnfTransformError
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
pub(all) enum BinaryOp {
|
||||||
|
Add
|
||||||
|
Sub
|
||||||
|
Mul
|
||||||
|
Div
|
||||||
|
Mod
|
||||||
|
Eq
|
||||||
|
NE
|
||||||
|
LT
|
||||||
|
GT
|
||||||
|
LE
|
||||||
|
GE
|
||||||
|
And
|
||||||
|
Or
|
||||||
|
}
|
||||||
|
impl Eq for BinaryOp
|
||||||
|
impl Show for BinaryOp
|
||||||
|
|
||||||
|
pub(all) struct Context {
|
||||||
|
mut name_env : Env
|
||||||
|
capture : Array[Name]
|
||||||
|
globals : Map[String, Type]
|
||||||
|
}
|
||||||
|
fn Context::add_intrinsic_functions(Self) -> Unit
|
||||||
|
fn Context::add_new_name(Self, String, Type) -> Name
|
||||||
|
fn Context::add_temp(Self, Type) -> Name
|
||||||
|
fn Context::apply_expr_to_knf(Self, @typecheck.ApplyExpr) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::assign_stmt_to_knf(Self, @typecheck.AssignStmt) -> Array[KnfStmt] raise KnfTransformError
|
||||||
|
fn Context::atom_expr_to_knf(Self, @typecheck.AtomExpr) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::binary_expr_to_knf(Self, BinaryOp, @typecheck.Expr, @typecheck.Expr) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::block_expr_to_knf(Self, @typecheck.BlockExpr) -> KnfBlock raise KnfTransformError
|
||||||
|
fn Context::enter_scope(Self) -> Unit
|
||||||
|
fn Context::exit_scope(Self) -> Unit
|
||||||
|
fn Context::expr_to_knf(Self, @typecheck.Expr) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::expr_to_knf_name(Self, KnfExpr, Type, Array[KnfStmt]) -> Name
|
||||||
|
fn Context::if_expr_to_knf(Self, @typecheck.IfExpr) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::left_value_to_knf(Self, @typecheck.LeftValue) -> (Array[KnfStmt], KnfExpr) raise KnfTransformError
|
||||||
|
fn Context::let_mut_stmt_to_knf(Self, @typecheck.LetMutStmt) -> Array[KnfStmt] raise KnfTransformError
|
||||||
|
fn Context::let_stmt_to_knf(Self, @typecheck.LetStmt) -> Array[KnfStmt] raise KnfTransformError
|
||||||
|
fn Context::local_function_to_knf(Self, @typecheck.LocalFunction) -> KnfClosure raise KnfTransformError
|
||||||
|
fn Context::lookup_name(Self, String) -> (Name, Type)?
|
||||||
|
fn Context::new() -> Self
|
||||||
|
fn Context::program_to_knf(Self, @typecheck.Program) -> KnfProgram raise KnfTransformError
|
||||||
|
fn Context::stmt_to_knf(Self, @typecheck.Stmt) -> Array[KnfStmt] raise KnfTransformError
|
||||||
|
fn Context::struct_def_to_knf(Self, @typecheck.StructDef) -> KnfStructDef raise KnfTransformError
|
||||||
|
fn Context::top_function_to_knf(Self, @typecheck.TopFunction) -> KnfFunction raise KnfTransformError
|
||||||
|
fn Context::top_let_to_knf(Self, @typecheck.TopLet) -> KnfTopLet raise KnfTransformError
|
||||||
|
fn Context::type_to_knf(Self, @typecheck.Type) -> Type raise KnfTransformError
|
||||||
|
fn Context::typekind_to_knf(Self, @typecheck.TypeKind) -> Type raise KnfTransformError
|
||||||
|
fn Context::while_stmt_to_knf(Self, @typecheck.WhileStmt) -> Array[KnfStmt] raise KnfTransformError
|
||||||
|
|
||||||
|
pub(all) struct Env {
|
||||||
|
local_ : Map[String, (Name, Type)]
|
||||||
|
capture : Map[Name, Type]
|
||||||
|
parent : Env?
|
||||||
|
}
|
||||||
|
fn Env::get(Self, String, no_capture? : Bool) -> Name?
|
||||||
|
fn Env::get_name_type(Self, Name) -> Type?
|
||||||
|
fn Env::new(parent? : Self?) -> Self
|
||||||
|
fn Env::set(Self, String, Name, Type) -> Unit
|
||||||
|
|
||||||
|
pub(all) struct KnfBlock {
|
||||||
|
stmts : Array[KnfStmt]
|
||||||
|
ty : Type
|
||||||
|
}
|
||||||
|
fn KnfBlock::nested_to_string(Self) -> String
|
||||||
|
fn KnfBlock::to_string(Self, Int) -> String
|
||||||
|
impl Show for KnfBlock
|
||||||
|
|
||||||
|
pub(all) struct KnfClosure {
|
||||||
|
name : Name
|
||||||
|
params : Array[(Name, Type)]
|
||||||
|
ret_ty : Type
|
||||||
|
body : KnfBlock
|
||||||
|
captured_vars : Map[Name, Type]
|
||||||
|
}
|
||||||
|
fn KnfClosure::to_string(Self, ident? : Int) -> String
|
||||||
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
fn KnfExpr::to_string(Self, ident? : Int) -> String
|
||||||
|
impl Show for KnfExpr
|
||||||
|
|
||||||
|
pub(all) struct KnfFunction {
|
||||||
|
name : String
|
||||||
|
ret_ty : Type
|
||||||
|
params : Array[(Name, Type)]
|
||||||
|
body : KnfBlock
|
||||||
|
}
|
||||||
|
impl Show for KnfFunction
|
||||||
|
|
||||||
|
pub(all) struct KnfProgram {
|
||||||
|
struct_defs : Map[String, KnfStructDef]
|
||||||
|
top_lets : Map[String, KnfTopLet]
|
||||||
|
functions : Map[String, KnfFunction]
|
||||||
|
}
|
||||||
|
impl Show for KnfProgram
|
||||||
|
|
||||||
|
pub(all) enum KnfStmt {
|
||||||
|
Let(Name, Type, KnfExpr)
|
||||||
|
LetMut(Name, Type, KnfExpr)
|
||||||
|
Assign(Name, KnfExpr)
|
||||||
|
ArrayPut(Name, Name, KnfExpr)
|
||||||
|
StructFieldSet(Name, String, Name)
|
||||||
|
While(KnfBlock, KnfBlock)
|
||||||
|
ExprStmt(KnfExpr)
|
||||||
|
Return(KnfExpr)
|
||||||
|
ReturnUnit
|
||||||
|
ClosureDef(KnfClosure)
|
||||||
|
}
|
||||||
|
fn KnfStmt::to_string(Self, ident? : Int) -> String
|
||||||
|
impl Show for KnfStmt
|
||||||
|
|
||||||
|
pub(all) struct KnfStructDef {
|
||||||
|
name : String
|
||||||
|
fields : Array[(String, Bool, Type)]
|
||||||
|
}
|
||||||
|
fn KnfStructDef::get_field_index(Self, String) -> Int?
|
||||||
|
impl Show for KnfStructDef
|
||||||
|
|
||||||
|
pub(all) struct KnfTopLet {
|
||||||
|
name : Name
|
||||||
|
ty : Type
|
||||||
|
expr : KnfExpr
|
||||||
|
init_stmts : Array[KnfStmt]
|
||||||
|
}
|
||||||
|
impl Show for KnfTopLet
|
||||||
|
|
||||||
|
pub(all) struct Name {
|
||||||
|
id : String
|
||||||
|
slot : Int
|
||||||
|
}
|
||||||
|
fn Name::wildcard() -> Self
|
||||||
|
impl Eq for Name
|
||||||
|
impl Hash for Name
|
||||||
|
impl Show for Name
|
||||||
|
|
||||||
|
pub(all) enum Type {
|
||||||
|
Unit
|
||||||
|
Int
|
||||||
|
Bool
|
||||||
|
Double
|
||||||
|
Array(Type)
|
||||||
|
Struct(String)
|
||||||
|
Tuple(Array[Type])
|
||||||
|
Function(Array[Type], Type)
|
||||||
|
}
|
||||||
|
impl Show for Type
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
@@ -474,6 +474,10 @@ pub fn parse_block_expr(
|
|||||||
}
|
}
|
||||||
let stmts = []
|
let stmts = []
|
||||||
let rest = loop rest {
|
let rest = loop rest {
|
||||||
|
[RCurlyBracket, .. r] => {
|
||||||
|
stmts.push(Expr(Literal(Unit)))
|
||||||
|
break r
|
||||||
|
}
|
||||||
r => {
|
r => {
|
||||||
let (stmt, is_end_expr, r) = parse_stmt_or_expr_end(r)
|
let (stmt, is_end_expr, r) = parse_stmt_or_expr_end(r)
|
||||||
stmts.push(stmt)
|
stmts.push(stmt)
|
||||||
|
|||||||
235
src/parser/pkg.generated.mbti
Normal file
235
src/parser/pkg.generated.mbti
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar/parser"
|
||||||
|
|
||||||
|
// Values
|
||||||
|
fn parse_add_sub_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_and_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_block_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_compare_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_enum_decl(ArrayView[Token]) -> (EnumDef, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_get_or_apply_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_if_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_if_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_let_stmt_type_expr(ArrayView[Token]) -> (Type?, Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_mul_div_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_optional_type_annotation(ArrayView[Token]) -> (Type?, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
#alias(parse_expr)
|
||||||
|
fn parse_or_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_program(Array[Token]) -> Program raise ParseError
|
||||||
|
|
||||||
|
fn parse_stmt_or_expr_end(ArrayView[Token]) -> (Stmt, Bool, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_struct_decl(ArrayView[Token]) -> (StructDef, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_type(ArrayView[Token]) -> (Type, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn parse_value_level_expr(ArrayView[Token]) -> (Expr, ArrayView[Token]) raise ParseError
|
||||||
|
|
||||||
|
fn tokenize(String) -> Array[Token] raise TokenizeError
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
pub(all) suberror ParseError String
|
||||||
|
impl Show for ParseError
|
||||||
|
|
||||||
|
pub(all) suberror TokenizeError String
|
||||||
|
impl Show for TokenizeError
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
pub(all) enum AddSubOp {
|
||||||
|
Add
|
||||||
|
Sub
|
||||||
|
}
|
||||||
|
impl Show for AddSubOp
|
||||||
|
|
||||||
|
pub(all) enum Binding {
|
||||||
|
Identifier(String)
|
||||||
|
Wildcard
|
||||||
|
Tuple(Array[Binding])
|
||||||
|
}
|
||||||
|
impl Show for Binding
|
||||||
|
|
||||||
|
pub(all) enum CompareOperator {
|
||||||
|
Equal
|
||||||
|
NotEqual
|
||||||
|
GreaterEqual
|
||||||
|
LessEqual
|
||||||
|
Greater
|
||||||
|
Less
|
||||||
|
}
|
||||||
|
impl Show for CompareOperator
|
||||||
|
|
||||||
|
pub(all) struct EnumDef {
|
||||||
|
id : String
|
||||||
|
user_defined_type : Type?
|
||||||
|
variants : Array[(String, Array[Type])]
|
||||||
|
}
|
||||||
|
impl Show for EnumDef
|
||||||
|
|
||||||
|
pub(all) enum Expr {
|
||||||
|
Or(Expr, Expr)
|
||||||
|
And(Expr, Expr)
|
||||||
|
Compare(CompareOperator, Expr, Expr)
|
||||||
|
AddSub(AddSubOp, Expr, Expr)
|
||||||
|
MulDivRem(MulDivRemOp, Expr, Expr)
|
||||||
|
Neg(Expr)
|
||||||
|
Not(Expr)
|
||||||
|
If(Expr, Expr, Expr?)
|
||||||
|
Match(Expr, Array[(Pattern, Expr)])
|
||||||
|
IndexAccess(Expr, Expr)
|
||||||
|
FieldAccess(Expr, String)
|
||||||
|
FunctionCall(Expr, Array[Expr])
|
||||||
|
ArrayMake(Expr, Expr)
|
||||||
|
StructConstruct(String, Array[(String, Expr)])
|
||||||
|
EnumConstruct(String?, String, Array[Expr])
|
||||||
|
Literal(Literal)
|
||||||
|
Tuple(Array[Expr])
|
||||||
|
Array(Array[Expr])
|
||||||
|
Identifier(String)
|
||||||
|
Block(Array[Stmt])
|
||||||
|
}
|
||||||
|
impl Show for Expr
|
||||||
|
|
||||||
|
pub(all) struct Function {
|
||||||
|
id : String
|
||||||
|
user_defined_type : Type?
|
||||||
|
params : Array[(String, Type?)]
|
||||||
|
return_type : Type?
|
||||||
|
body : Array[Stmt]
|
||||||
|
}
|
||||||
|
impl Show for Function
|
||||||
|
|
||||||
|
pub(all) enum Literal {
|
||||||
|
Unit
|
||||||
|
Bool(Bool)
|
||||||
|
Int(Int)
|
||||||
|
Double(Double)
|
||||||
|
}
|
||||||
|
impl Show for Literal
|
||||||
|
|
||||||
|
pub(all) enum MulDivRemOp {
|
||||||
|
Mul
|
||||||
|
Div
|
||||||
|
Rem
|
||||||
|
}
|
||||||
|
impl Show for MulDivRemOp
|
||||||
|
|
||||||
|
pub(all) enum Pattern {
|
||||||
|
Wildcard
|
||||||
|
Identifier(String)
|
||||||
|
Literal(Literal)
|
||||||
|
Tuple(Array[Pattern])
|
||||||
|
Enum(String?, String, Array[Pattern])
|
||||||
|
}
|
||||||
|
impl Show for Pattern
|
||||||
|
|
||||||
|
pub(all) struct Program {
|
||||||
|
top_lets : Map[String, TopLet]
|
||||||
|
top_functions : Map[String, Function]
|
||||||
|
struct_defs : Map[String, StructDef]
|
||||||
|
enum_defs : Map[String, EnumDef]
|
||||||
|
}
|
||||||
|
impl Show for Program
|
||||||
|
|
||||||
|
pub(all) enum Stmt {
|
||||||
|
Let(Binding, Type?, Expr)
|
||||||
|
LetMut(String, Type?, Expr)
|
||||||
|
Assign(Expr, Expr)
|
||||||
|
While(Expr, Array[Stmt])
|
||||||
|
Expr(Expr)
|
||||||
|
Return(Expr)
|
||||||
|
LocalFunction(Function)
|
||||||
|
}
|
||||||
|
impl Show for Stmt
|
||||||
|
|
||||||
|
pub(all) struct StructDef {
|
||||||
|
id : String
|
||||||
|
user_defined_type : Type?
|
||||||
|
fields : Array[(String, Type)]
|
||||||
|
}
|
||||||
|
impl Show for StructDef
|
||||||
|
|
||||||
|
pub(all) enum Token {
|
||||||
|
EOF
|
||||||
|
Unit
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Double
|
||||||
|
Array
|
||||||
|
Not
|
||||||
|
If
|
||||||
|
Else
|
||||||
|
Match
|
||||||
|
While
|
||||||
|
Fn
|
||||||
|
Return
|
||||||
|
Let
|
||||||
|
Mut
|
||||||
|
Struct
|
||||||
|
Enum
|
||||||
|
BoolLiteral(Bool)
|
||||||
|
IntLiteral(Int)
|
||||||
|
DoubleLiteral(Double)
|
||||||
|
UpperIdentifier(String)
|
||||||
|
LowerIdentifier(String)
|
||||||
|
Wildcard
|
||||||
|
CompareOperator(CompareOperator)
|
||||||
|
And
|
||||||
|
Or
|
||||||
|
Dot
|
||||||
|
Add
|
||||||
|
Sub
|
||||||
|
Mul
|
||||||
|
Div
|
||||||
|
Rem
|
||||||
|
Assign
|
||||||
|
LParen
|
||||||
|
RParen
|
||||||
|
LBracket
|
||||||
|
RBracket
|
||||||
|
LCurlyBracket
|
||||||
|
RCurlyBracket
|
||||||
|
Arrow
|
||||||
|
MatchArrow
|
||||||
|
DoubleColon
|
||||||
|
Colon
|
||||||
|
Semicolon
|
||||||
|
Comma
|
||||||
|
}
|
||||||
|
impl Show for Token
|
||||||
|
|
||||||
|
pub(all) struct TopLet {
|
||||||
|
id : String
|
||||||
|
type_ : Type?
|
||||||
|
expr : Expr
|
||||||
|
}
|
||||||
|
impl Show for TopLet
|
||||||
|
|
||||||
|
pub(all) enum Type {
|
||||||
|
Unit
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Double
|
||||||
|
Array(Type)
|
||||||
|
Tuple(Array[Type])
|
||||||
|
Function(Array[Type], Type)
|
||||||
|
UserDefined(String)
|
||||||
|
Generic(String, Type)
|
||||||
|
}
|
||||||
|
impl Show for Type
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
13
src/pkg.generated.mbti
Normal file
13
src/pkg.generated.mbti
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar"
|
||||||
|
|
||||||
|
// Values
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
@@ -16,15 +16,21 @@ pub fn Context::check_block_expr(
|
|||||||
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()
|
||||||
guard checked_stmts is [.., { kind: ExprStmt(expr) }] else {
|
guard checked_stmts is [.., { kind: ExprStmt(expr) }] else {
|
||||||
return { stmts: checked_stmts, ty: Unit }
|
checked_stmts.push({
|
||||||
}
|
kind: ExprStmt({
|
||||||
if checked_stmts is [.., { kind: ReturnStmt(ret) }] {
|
kind: ApplyExpr({ kind: AtomExpr({ kind: Unit, ty: Unit }), ty: Unit }),
|
||||||
{ stmts: checked_stmts, ty: ret.ty }
|
ty: Unit,
|
||||||
} else if checked_stmts is [.., stmt1, stmt2] &&
|
}),
|
||||||
stmt2.kind is ExprStmt({ ty: Unit, .. }) &&
|
})
|
||||||
stmt1.kind is ReturnStmt(ret) {
|
{ stmts: checked_stmts, ty: Unit }
|
||||||
{ stmts: checked_stmts, ty: ret.ty }
|
|
||||||
} else {
|
|
||||||
{ stmts: checked_stmts, ty: expr.ty }
|
|
||||||
}
|
}
|
||||||
|
// 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 }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
304
src/typecheck/pkg.generated.mbti
Normal file
304
src/typecheck/pkg.generated.mbti
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
// Generated using `moon info`, DON'T EDIT IT
|
||||||
|
package "Lil-Ran/lilunar/typecheck"
|
||||||
|
|
||||||
|
import(
|
||||||
|
"Lil-Ran/lilunar/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Values
|
||||||
|
fn parser_pattern_map(@parser.Binding) -> Pattern
|
||||||
|
|
||||||
|
fn typecheck(@parser.Program) -> Program raise TypeCheckError
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
pub(all) suberror TypeCheckError String
|
||||||
|
impl Show for TypeCheckError
|
||||||
|
|
||||||
|
// Types and methods
|
||||||
|
pub(all) enum AddSubOp {
|
||||||
|
Add
|
||||||
|
Sub
|
||||||
|
}
|
||||||
|
impl Show for AddSubOp
|
||||||
|
|
||||||
|
pub(all) struct ApplyExpr {
|
||||||
|
kind : ApplyExprKind
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for ApplyExpr
|
||||||
|
|
||||||
|
pub(all) enum ApplyExprKind {
|
||||||
|
AtomExpr(AtomExpr)
|
||||||
|
ArrayAccess(ApplyExpr, Expr)
|
||||||
|
FieldAccess(ApplyExpr, String)
|
||||||
|
Call(ApplyExpr, Array[Expr])
|
||||||
|
}
|
||||||
|
impl Show for ApplyExprKind
|
||||||
|
|
||||||
|
pub(all) struct AssignStmt {
|
||||||
|
left_value : LeftValue
|
||||||
|
expr : Expr
|
||||||
|
}
|
||||||
|
impl Show for AssignStmt
|
||||||
|
|
||||||
|
pub(all) struct AtomExpr {
|
||||||
|
kind : AtomExprKind
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for AtomExpr
|
||||||
|
|
||||||
|
pub(all) enum AtomExprKind {
|
||||||
|
Int(Int)
|
||||||
|
Double(Double)
|
||||||
|
Bool(Bool)
|
||||||
|
Unit
|
||||||
|
Ident(String)
|
||||||
|
Tuple(Array[Expr])
|
||||||
|
Array(Array[Expr])
|
||||||
|
ArrayMake(Expr, Expr)
|
||||||
|
StructConstruct(StructConstructExpr)
|
||||||
|
}
|
||||||
|
impl Show for AtomExprKind
|
||||||
|
|
||||||
|
pub(all) struct BlockExpr {
|
||||||
|
stmts : Array[Stmt]
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for BlockExpr
|
||||||
|
|
||||||
|
pub(all) enum CompareOperator {
|
||||||
|
Equal
|
||||||
|
NotEqual
|
||||||
|
GreaterEqual
|
||||||
|
LessEqual
|
||||||
|
Greater
|
||||||
|
Less
|
||||||
|
}
|
||||||
|
impl Show for CompareOperator
|
||||||
|
|
||||||
|
pub(all) struct Context {
|
||||||
|
mut type_env : Env
|
||||||
|
type_vars : Map[Int, TypeKind]
|
||||||
|
struct_defs : Map[String, StructDef]
|
||||||
|
func_types : Map[String, TypeKind]
|
||||||
|
mut current_func_ret_ty : TypeKind?
|
||||||
|
}
|
||||||
|
fn Context::add_intrinsic_functions(Self) -> Unit
|
||||||
|
fn Context::check_apply_expr(Self, @parser.Expr) -> ApplyExpr raise TypeCheckError
|
||||||
|
fn Context::check_assign_stmt(Self, @parser.Stmt) -> AssignStmt raise TypeCheckError
|
||||||
|
fn Context::check_atom_expr(Self, @parser.Expr) -> AtomExpr raise TypeCheckError
|
||||||
|
fn Context::check_block_expr(Self, @parser.Expr) -> BlockExpr raise TypeCheckError
|
||||||
|
fn Context::check_expr(Self, @parser.Expr) -> Expr raise TypeCheckError
|
||||||
|
fn Context::check_if_expr(Self, @parser.Expr) -> IfExpr raise TypeCheckError
|
||||||
|
fn Context::check_left_value(Self, @parser.Expr) -> LeftValue raise TypeCheckError
|
||||||
|
fn Context::check_let_mut_stmt(Self, @parser.Stmt) -> LetMutStmt raise TypeCheckError
|
||||||
|
fn Context::check_let_stmt(Self, @parser.Stmt) -> LetStmt raise TypeCheckError
|
||||||
|
fn Context::check_local_function(Self, @parser.Function) -> LocalFunction raise TypeCheckError
|
||||||
|
fn Context::check_parser_type(Self, @parser.Type, mutable? : Bool) -> Type raise TypeCheckError
|
||||||
|
fn Context::check_program(Self, @parser.Program) -> Program raise TypeCheckError
|
||||||
|
fn Context::check_stmt(Self, @parser.Stmt) -> Stmt raise TypeCheckError
|
||||||
|
fn Context::check_struct_def(Self, @parser.StructDef) -> StructDef raise TypeCheckError
|
||||||
|
fn Context::check_top_function_body(Self, @parser.Function) -> TopFunction raise TypeCheckError
|
||||||
|
fn Context::check_top_function_type_decl(Self, @parser.Function) -> TypeKind raise TypeCheckError
|
||||||
|
fn Context::check_top_let(Self, @parser.TopLet) -> TopLet raise TypeCheckError
|
||||||
|
fn Context::check_while_stmt(Self, @parser.Stmt) -> WhileStmt raise TypeCheckError
|
||||||
|
fn Context::deref_type_var(Self, TypeKind) -> TypeKind
|
||||||
|
fn Context::enter_scope(Self) -> Unit
|
||||||
|
fn Context::exit_scope(Self) -> Unit
|
||||||
|
fn Context::is_type_compatible(Self, TypeKind, TypeKind) -> Bool
|
||||||
|
fn Context::lookup_type(Self, String) -> Type?
|
||||||
|
fn Context::new() -> Self
|
||||||
|
fn Context::new_type_var(Self) -> TypeKind
|
||||||
|
#deprecated
|
||||||
|
fn Context::set_current_func_ret_ty(Self, TypeKind) -> Unit
|
||||||
|
fn Context::set_pattern_types(Self, Pattern, TypeKind) -> Unit raise TypeCheckError
|
||||||
|
fn Context::substitute_type_var(Self, Program) -> Program
|
||||||
|
fn Context::substitute_type_var_for_expr(Self, Expr) -> Expr
|
||||||
|
|
||||||
|
pub(all) struct Env {
|
||||||
|
local_ : Map[String, Type]
|
||||||
|
parent : Env?
|
||||||
|
}
|
||||||
|
fn Env::get(Self, String) -> Type?
|
||||||
|
fn Env::new(parent? : Self?) -> Self
|
||||||
|
fn Env::set(Self, String, Type) -> Unit
|
||||||
|
|
||||||
|
pub(all) struct Expr {
|
||||||
|
kind : ExprKind
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for Expr
|
||||||
|
|
||||||
|
pub(all) enum ExprKind {
|
||||||
|
ApplyExpr(ApplyExpr)
|
||||||
|
BlockExpr(BlockExpr)
|
||||||
|
NotExpr(Expr)
|
||||||
|
NegExpr(Expr)
|
||||||
|
Compare(CompareOperator, Expr, Expr)
|
||||||
|
AddSub(AddSubOp, Expr, Expr)
|
||||||
|
MulDivRem(MulDivRemOp, Expr, Expr)
|
||||||
|
And(Expr, Expr)
|
||||||
|
Or(Expr, Expr)
|
||||||
|
IfExpr(IfExpr)
|
||||||
|
}
|
||||||
|
impl Show for ExprKind
|
||||||
|
|
||||||
|
pub(all) struct IfExpr {
|
||||||
|
cond : Expr
|
||||||
|
then_block : BlockExpr
|
||||||
|
else_block : Expr?
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for IfExpr
|
||||||
|
|
||||||
|
pub(all) struct LeftValue {
|
||||||
|
kind : LeftValueKind
|
||||||
|
ty : Type
|
||||||
|
}
|
||||||
|
impl Show for LeftValue
|
||||||
|
|
||||||
|
pub(all) enum LeftValueKind {
|
||||||
|
Ident(String)
|
||||||
|
ArrayAccess(LeftValue, Expr)
|
||||||
|
FieldAccess(LeftValue, String)
|
||||||
|
}
|
||||||
|
impl Show for LeftValueKind
|
||||||
|
|
||||||
|
pub(all) struct LetMutStmt {
|
||||||
|
name : String
|
||||||
|
ty : Type
|
||||||
|
expr : Expr
|
||||||
|
}
|
||||||
|
impl Show for LetMutStmt
|
||||||
|
|
||||||
|
pub(all) struct LetStmt {
|
||||||
|
pattern : Pattern
|
||||||
|
ty : TypeKind
|
||||||
|
expr : Expr
|
||||||
|
}
|
||||||
|
impl Show for LetStmt
|
||||||
|
|
||||||
|
pub(all) struct LocalFunction {
|
||||||
|
fname : String
|
||||||
|
param_list : Array[(String, Type)]
|
||||||
|
ret_ty : Type
|
||||||
|
body : BlockExpr
|
||||||
|
}
|
||||||
|
impl Show for LocalFunction
|
||||||
|
|
||||||
|
pub(all) enum MulDivRemOp {
|
||||||
|
Mul
|
||||||
|
Div
|
||||||
|
Rem
|
||||||
|
}
|
||||||
|
impl Show for MulDivRemOp
|
||||||
|
|
||||||
|
pub(all) struct Param {
|
||||||
|
name : String
|
||||||
|
ty : TypeKind
|
||||||
|
}
|
||||||
|
impl Show for Param
|
||||||
|
|
||||||
|
pub(all) struct Pattern {
|
||||||
|
kind : PatternKind
|
||||||
|
}
|
||||||
|
impl Eq for Pattern
|
||||||
|
impl Show for Pattern
|
||||||
|
|
||||||
|
pub(all) enum PatternKind {
|
||||||
|
Wildcard
|
||||||
|
Ident(String)
|
||||||
|
Tuple(Array[Pattern])
|
||||||
|
}
|
||||||
|
impl Eq for PatternKind
|
||||||
|
impl Show for PatternKind
|
||||||
|
|
||||||
|
pub(all) struct Program {
|
||||||
|
top_lets : Map[String, TopLet]
|
||||||
|
top_functions : Map[String, TopFunction]
|
||||||
|
struct_defs : Map[String, StructDef]
|
||||||
|
}
|
||||||
|
impl Show for Program
|
||||||
|
|
||||||
|
pub(all) struct Stmt {
|
||||||
|
kind : StmtKind
|
||||||
|
}
|
||||||
|
impl Show for Stmt
|
||||||
|
|
||||||
|
pub(all) enum StmtKind {
|
||||||
|
LetStmt(LetStmt)
|
||||||
|
LetMutStmt(LetMutStmt)
|
||||||
|
AssignStmt(AssignStmt)
|
||||||
|
WhileStmt(WhileStmt)
|
||||||
|
ExprStmt(Expr)
|
||||||
|
ReturnStmt(Expr)
|
||||||
|
LocalFunction(LocalFunction)
|
||||||
|
}
|
||||||
|
impl Show for StmtKind
|
||||||
|
|
||||||
|
pub(all) struct StructConstructExpr {
|
||||||
|
name : String
|
||||||
|
fields : Array[(String, Expr)]
|
||||||
|
}
|
||||||
|
impl Show for StructConstructExpr
|
||||||
|
|
||||||
|
pub(all) struct StructDef {
|
||||||
|
name : String
|
||||||
|
fields : Array[StructField]
|
||||||
|
}
|
||||||
|
fn StructDef::get_field_type(Self, String) -> Type?
|
||||||
|
impl Show for StructDef
|
||||||
|
|
||||||
|
pub(all) struct StructField {
|
||||||
|
name : String
|
||||||
|
ty : Type
|
||||||
|
}
|
||||||
|
impl Show for StructField
|
||||||
|
|
||||||
|
pub(all) struct TopFunction {
|
||||||
|
fname : String
|
||||||
|
param_list : Array[Param]
|
||||||
|
ty : TypeKind
|
||||||
|
body : BlockExpr
|
||||||
|
}
|
||||||
|
impl Show for TopFunction
|
||||||
|
|
||||||
|
pub(all) struct TopLet {
|
||||||
|
name : String
|
||||||
|
ty : Type
|
||||||
|
expr : Expr
|
||||||
|
}
|
||||||
|
impl Show for TopLet
|
||||||
|
|
||||||
|
pub(all) struct Type {
|
||||||
|
kind : TypeKind
|
||||||
|
mutable : Bool
|
||||||
|
}
|
||||||
|
impl Show for Type
|
||||||
|
|
||||||
|
pub(all) enum TypeKind {
|
||||||
|
Unit
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Double
|
||||||
|
Tuple(Array[TypeKind])
|
||||||
|
Array(TypeKind)
|
||||||
|
Function(Array[TypeKind], TypeKind)
|
||||||
|
Struct(String)
|
||||||
|
Any
|
||||||
|
TypeVar(Int)
|
||||||
|
}
|
||||||
|
impl Eq for TypeKind
|
||||||
|
impl Hash for TypeKind
|
||||||
|
impl Show for TypeKind
|
||||||
|
|
||||||
|
pub(all) struct WhileStmt {
|
||||||
|
cond : Expr
|
||||||
|
body : BlockExpr
|
||||||
|
}
|
||||||
|
impl Show for WhileStmt
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
|
||||||
Reference in New Issue
Block a user