This commit is contained in:
2025-10-03 23:47:21 +08:00
parent 5599a24065
commit 582f22eb33
5 changed files with 457 additions and 0 deletions

View File

@@ -1,3 +1,11 @@
# Lilunar # Lilunar
Lil-Ran's MiniMoonBit to RISC-V compiler for [MGPIC-2025](https://www.moonbitlang.cn/2025-mgpic-compiler). Lil-Ran's MiniMoonBit to RISC-V compiler for [MGPIC-2025](https://www.moonbitlang.cn/2025-mgpic-compiler).
It is optimized for runtime speed and code size, but not for compilation speed, according to the contest rules.
## Reference
https://github.com/moonbitlang/contest-2025-data
https://github.com/moonbitlang/minimoonbit-public

148
src/parser/ast.mbt Normal file
View File

@@ -0,0 +1,148 @@
///|
suberror ParseError String
// region Context
// region Type
///|
pub(all) enum Type {
Unit
Bool
Int
Double
Array(Type)
Tuple(Array[Type])
Function(Array[Type], Type)
UserDefined(String)
Generic(String, Type)
} derive(Show)
// region Components
///|
pub(all) enum Literal {
Unit
Bool(Bool)
Int(Int)
Double(Double)
} derive(Show)
///|
enum LeftValue {
Identifier(String)
FieldAccess(LeftValue, String)
IndexAccess(LeftValue, Expr)
} derive(Show)
///|
enum AddSubOp {
Add
Sub
} derive(Show)
///|
enum MulDivRemOp {
Mul
Div
Rem
} derive(Show)
///|
enum Expr {
Or(Expr, Expr)
And(Expr, Expr)
Compare(CompareOperator, Expr, Expr)
AddSub(AddSubOp, Expr, Expr)
MulDivRem(MulDivRemOp, Expr, Expr)
If(Expr, Expr, Expr?)
Match(Expr, Array[(Pattern, Expr)])
} derive(Show)
///|
enum TopLevel {
TopLetDecl(id~ : String, type_~ : Type?, expr~ : Expr)
TopFn(Function)
Struct(Struct)
Enum(Enum)
}
///|
struct Program(Array[TopLevel])
///|
fn parse_type(
tokens : ArrayView[Token],
) -> (Type, ArrayView[Token]) raise ParseError {
match tokens {
[Unit, .. rest] => (Unit, rest)
[Bool, .. rest] => (Bool, rest)
[Int, .. rest] => (Int, rest)
[Double, .. rest] => (Double, rest)
[Array, LBracket, .. rest] => {
let (elem_type, rest) = parse_type(rest)
guard rest is [RBracket, .. rest] else {
raise ParseError("Expected ']' after array type")
}
(Array(elem_type), rest)
}
[LParen, .. rest] => {
let (first_type, rest) = parse_type(rest)
let types = [first_type]
loop rest {
[Comma, .. r] => {
let (next_type, r) = parse_type(r)
types.push(next_type)
continue r
}
[RParen, Arrow, .. r] => {
let (return_type, r) = parse_type(r)
(Function(types, return_type), r)
}
[RParen, .. r] => (Tuple(types), r)
_ =>
raise ParseError(
"Expected ',' or ')' or ')' '->' in tuple/function type",
)
}
}
[UpperIdentifier(name), LBracket, .. rest] => {
let (type_arg, rest) = parse_type(rest)
guard rest is [RBracket, .. rest] else {
raise ParseError("Expected ']' after generic type argument")
}
(Generic(name, type_arg), rest)
}
[UpperIdentifier(name), .. rest] => (UserDefined(name), rest)
_ => raise ParseError("Unexpected token while parsing type")
}
}
///|
pub fn parse_program(tokens : Array[Token]) -> Program raise ParseError {
let program = []
loop tokens[:] {
[EOF] => program
[Let, LowerIdentifier(id) | UpperIdentifier(id), Colon, .. rest] => {
let (type_, rest) = parse_type(rest)
guard rest is [Assign, .. rest] else {
raise ParseError(
"Expected '=' after type annotation in let declaration",
)
}
let (expr, rest) = parse_expr(rest)
program.push(TopLetDecl(id, type_, expr))
continue rest
}
[Let, LowerIdentifier(id) | UpperIdentifier(id), Assign, .. rest] => {
let (expr, rest) = parse_expr(rest)
program.push(TopLetDecl(id, None, expr))
continue rest
}
[Fn, LowerIdentifier("main"), ..] => ...
[Fn, ..] => ...
[Struct, ..] => ...
[Enum, ..] => ...
_ => raise ParseError("Unexpected token at top level")
}
}

56
src/parser/ast_wbtest.mbt Normal file
View File

@@ -0,0 +1,56 @@
///|
test "parse_type" {
inspect(parse_type([Unit, EOF]), content="(Unit, [EOF])")
inspect(parse_type([Bool, EOF]), content="(Bool, [EOF])")
inspect(parse_type([Int, EOF]), content="(Int, [EOF])")
inspect(parse_type([Double, EOF]), content="(Double, [EOF])")
inspect(
parse_type([Array, LBracket, Int, RBracket, EOF]),
content="(Array(Int), [EOF])",
)
inspect(
parse_type([LParen, Int, Comma, Bool, RParen, EOF]),
content="(Tuple([Int, Bool]), [EOF])",
)
inspect(
parse_type([LParen, Int, RParen, EOF]),
content="(Tuple([Int]), [EOF])",
)
inspect(
parse_type([LParen, Int, Comma, Bool, RParen, Arrow, Double, EOF]),
content="(Function([Int, Bool], Double), [EOF])",
)
inspect(
parse_type([UpperIdentifier("MyType"), EOF]),
content=(
#|(UserDefined("MyType"), [EOF])
),
)
inspect(
parse_type([
UpperIdentifier("A"),
LBracket,
UpperIdentifier("B"),
RBracket,
EOF,
]),
content=(
#|(Generic("A", UserDefined("B")), [EOF])
),
)
inspect(
parse_type([
UpperIdentifier("A"),
LBracket,
UpperIdentifier("B"),
LBracket,
UpperIdentifier("C"),
RBracket,
RBracket,
EOF,
]),
content=(
#|(Generic("A", Generic("B", UserDefined("C"))), [EOF])
),
)
}

1
src/parser/moon.pkg.json Normal file
View File

@@ -0,0 +1 @@
{}

244
src/parser/tokenize.mbt Normal file
View File

@@ -0,0 +1,244 @@
///|
pub(all) enum CompareOperator {
Equal
NotEqual
GreaterEqual
LessEqual
Greater
Less
} derive(Show)
///|
pub(all) enum Token {
EOF
BoolLiteral(Bool)
Unit
Bool
Int
Double
Array
Not
If
Else
Fn
Let
Struct
Enum
Number(Int)
UpperIdentifier(String)
LowerIdentifier(String)
Wildcard
CompareOperator(CompareOperator)
And
Or
Dot
Add
Sub
Mul
Div
Assign
LParen
RParen
LBracket
RBracket
LCurlyBracket
RCurlyBracket
Arrow
Colon
Semicolon
Comma
} derive(Show)
///|
pub fn tokenize(input : String) -> Array[Token] {
let tokens = []
loop input[:] {
[' ' | '\n' | '\r' | '\t', .. rest] => continue rest
[.. "//", .. rest] => {
let rest = loop rest {
['\n' | '\r', .. r] => r
[_, .. r] => continue r
[] => []
}
continue rest
}
['0'..='9', ..] as pattern => {
let number_str = StringBuilder::new()
let rest = loop pattern {
['0'..='9' as c, .. r] => {
number_str.write_char(c)
continue r
}
r => {
let number = try! @strconv.parse_int(number_str.to_string())
tokens.push(Number(number))
r
}
}
continue rest
}
['A'..='Z', ..] as pattern => {
let ident_str = StringBuilder::new()
let rest = loop pattern {
['a'..='z' | 'A'..='Z' | '0'..='9' | '_' as c, .. r] => {
ident_str.write_char(c)
continue r
}
r => {
let ident : Token = match ident_str.to_string() {
"Unit" => Unit
"Bool" => Bool
"Int" => Int
"Double" => Double
"Array" => Array
s => UpperIdentifier(s)
}
tokens.push(ident)
r
}
}
continue rest
}
['a'..='z', ..] | ['_', ..] as pattern => {
let ident_str = StringBuilder::new()
let rest = loop pattern {
['a'..='z' | 'A'..='Z' | '0'..='9' | '_' as c, .. r] => {
ident_str.write_char(c)
continue r
}
r => {
let ident = match ident_str.to_string() {
"_" => Wildcard
"true" => BoolLiteral(true)
"false" => BoolLiteral(false)
"not" => Not
"if" => If
"else" => Else
"fn" => Fn
"let" => Let
"struct" => Struct
"enum" => Enum
s => LowerIdentifier(s)
}
tokens.push(ident)
r
}
}
continue rest
}
[.. "->", .. rest] => {
tokens.push(Arrow)
continue rest
}
[.. "==", .. rest] => {
tokens.push(CompareOperator(Equal))
continue rest
}
[.. "!=", .. rest] => {
tokens.push(CompareOperator(NotEqual))
continue rest
}
[.. ">=", .. rest] => {
tokens.push(CompareOperator(GreaterEqual))
continue rest
}
[.. "<=", .. rest] => {
tokens.push(CompareOperator(LessEqual))
continue rest
}
['>', .. rest] => {
tokens.push(CompareOperator(Greater))
continue rest
}
['<', .. rest] => {
tokens.push(CompareOperator(Less))
continue rest
}
[.. "&&", .. rest] => {
tokens.push(And)
continue rest
}
[.. "||", .. rest] => {
tokens.push(Or)
continue rest
}
['.', .. rest] => {
tokens.push(Dot)
continue rest
}
['+', .. rest] => {
tokens.push(Add)
continue rest
}
['-', .. rest] => {
tokens.push(Sub)
continue rest
}
['*', .. rest] => {
tokens.push(Mul)
continue rest
}
['/', .. rest] => {
tokens.push(Div)
continue rest
}
['=', .. rest] => {
tokens.push(Assign)
continue rest
}
['(', .. rest] => {
tokens.push(LParen)
continue rest
}
[')', .. rest] => {
tokens.push(RParen)
continue rest
}
['[', .. rest] => {
tokens.push(LBracket)
continue rest
}
[']', .. rest] => {
tokens.push(RBracket)
continue rest
}
['{', .. rest] => {
tokens.push(LCurlyBracket)
continue rest
}
['}', .. rest] => {
tokens.push(RCurlyBracket)
continue rest
}
[':', .. rest] => {
tokens.push(Colon)
continue rest
}
[';', .. rest] => {
tokens.push(Semicolon)
continue rest
}
[',', .. rest] => {
tokens.push(Comma)
continue rest
}
[] => tokens.push(EOF)
[c, ..] => abort("Unexpected character: " + c.to_string())
}
tokens
}
///|
test "tokenize" {
let input =
#| let x = 42 // A comment.
#| if x > 0 {
#| x = x - 1
#| } else { x = 0 }
inspect(
tokenize(input),
content=(
#|[Let, LowerIdentifier("x"), Assign, Number(42), If, LowerIdentifier("x"), CompareOperator(Greater), Number(0), LCurlyBracket, LowerIdentifier("x"), Assign, LowerIdentifier("x"), Sub, Number(1), RCurlyBracket, Else, LCurlyBracket, LowerIdentifier("x"), Assign, Number(0), RCurlyBracket, EOF]
),
)
}