feat: restructure repo, borrow checker, expanded stdlib
- Reorganize repository to Rust-style layout: compiler/bootstrap/ compiler/selfhost/ compiler/tests/ library/std/ library/runtime/ tests/ tools/ - Add buxs/ Windows-compatible project root - Add borrow checker tests and implement: - Alias analysis (double mutable borrow detection) - Use-after-move detection for own T - Expand standard library: - Std::Os: Args, Env, Cwd, Chdir - Std::Time: NowMs, NowUs, SleepMs - Std::Process: Run, Output - Std::Io: PrintInt64 (fixes 32-bit truncation bug) - Add examples: os_time.bux, process.bux - Fix PrintInt to use int64_t in C runtime
This commit is contained in:
Executable
BIN
Binary file not shown.
@@ -0,0 +1,117 @@
|
||||
import std/[unittest, strutils]
|
||||
import ../bootstrap/[lexer, parser, sema, types]
|
||||
|
||||
proc checkSource(source: string): SemaResult =
|
||||
let lexRes = tokenize(source, "<test>")
|
||||
check not lexRes.hasErrors
|
||||
let parseRes = parse(lexRes.tokens, "<test>")
|
||||
check parseRes.diagnostics.len == 0
|
||||
result = analyze(parseRes.module)
|
||||
|
||||
suite "Borrow Checker":
|
||||
|
||||
test "@[Checked] function with &mut allows mutation":
|
||||
let res = checkSource("""
|
||||
func Mutate(val: &mut int) {
|
||||
*val = 42;
|
||||
}
|
||||
func Main() -> int {
|
||||
var x: int = 10;
|
||||
Mutate(&x);
|
||||
return 0;
|
||||
}
|
||||
""")
|
||||
check(not res.hasErrors)
|
||||
|
||||
test "@[Checked] function rejects write through &T":
|
||||
let res = checkSource("""
|
||||
@[Checked]
|
||||
func BadWrite(val: &int) {
|
||||
*val = 42;
|
||||
}
|
||||
func Main() -> int {
|
||||
var x: int = 10;
|
||||
BadWrite(&x);
|
||||
return 0;
|
||||
}
|
||||
""")
|
||||
check(res.hasErrors)
|
||||
check(res.diagnostics[0].message.contains("cannot assign through shared reference"))
|
||||
|
||||
test "unchecked function allows write through raw pointer":
|
||||
let res = checkSource("""
|
||||
func RawWrite(val: *int) {
|
||||
*val = 42;
|
||||
}
|
||||
func Main() -> int {
|
||||
var x: int = 10;
|
||||
RawWrite(&x);
|
||||
return 0;
|
||||
}
|
||||
""")
|
||||
check(not res.hasErrors)
|
||||
|
||||
test "&T allows reading":
|
||||
let res = checkSource("""
|
||||
@[Checked]
|
||||
func Read(val: &int) -> int {
|
||||
return *val;
|
||||
}
|
||||
func Main() -> int {
|
||||
var x: int = 10;
|
||||
return Read(&x);
|
||||
}
|
||||
""")
|
||||
check(not res.hasErrors)
|
||||
|
||||
test "own T type parses and resolves":
|
||||
let res = checkSource("""
|
||||
struct Box {
|
||||
value: int;
|
||||
}
|
||||
func TakeOwn(b: own Box) -> int {
|
||||
return b.value;
|
||||
}
|
||||
func Main() -> int {
|
||||
var b: Box = Box { value: 42 };
|
||||
return TakeOwn(b);
|
||||
}
|
||||
""")
|
||||
check(not res.hasErrors)
|
||||
|
||||
test "@[Checked] rejects double mutable borrow in call":
|
||||
let res = checkSource("""
|
||||
@[Checked]
|
||||
func Swap(a: &mut int, b: &mut int) {
|
||||
let tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
@[Checked]
|
||||
func Main() -> int {
|
||||
var x: int = 10;
|
||||
Swap(&x, &x);
|
||||
return 0;
|
||||
}
|
||||
""")
|
||||
check(res.hasErrors)
|
||||
check(res.diagnostics[0].message.contains("mutable borrow"))
|
||||
|
||||
test "@[Checked] rejects use after move":
|
||||
let res = checkSource("""
|
||||
struct Box {
|
||||
value: int;
|
||||
}
|
||||
@[Checked]
|
||||
func Consume(b: own Box) -> int {
|
||||
return b.value;
|
||||
}
|
||||
@[Checked]
|
||||
func Main() -> int {
|
||||
var b: Box = Box { value: 42 };
|
||||
let x: int = Consume(b);
|
||||
return b.value;
|
||||
}
|
||||
""")
|
||||
check(res.hasErrors)
|
||||
check(res.diagnostics[0].message.contains("moved"))
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,104 @@
|
||||
import std/[unittest, strformat]
|
||||
import ../bootstrap/[lexer, parser, sema, hir, hir_lower, types, scope]
|
||||
|
||||
proc lowerSource(source: string): HirModule =
|
||||
let lexRes = tokenize(source, "<test>")
|
||||
check not lexRes.hasErrors
|
||||
let parseRes = parse(lexRes.tokens, "<test>")
|
||||
check parseRes.diagnostics.len == 0
|
||||
# Create Sema and populate global scope + method table
|
||||
var s = Sema(module: parseRes.module, globalScope: newScope())
|
||||
s.collectGlobals()
|
||||
result = lowerModule(parseRes.module, s)
|
||||
|
||||
suite "HIR Lowering":
|
||||
test "simple function":
|
||||
let src = "func Main() -> int { return 0; }"
|
||||
let hir = lowerSource(src)
|
||||
check hir.funcs.len == 1
|
||||
check hir.funcs[0].name == "Main"
|
||||
check hir.funcs[0].body != nil
|
||||
check hir.funcs[0].body.kind == hBlock
|
||||
echo " PASS: simple function"
|
||||
|
||||
test "function with arithmetic":
|
||||
let src = "func Add(a: int, b: int) -> int { return a + b; }"
|
||||
let hir = lowerSource(src)
|
||||
check hir.funcs.len == 1
|
||||
check hir.funcs[0].params.len == 2
|
||||
echo " PASS: function with arithmetic"
|
||||
|
||||
test "struct lowering":
|
||||
let src = """
|
||||
struct Point { x: float64; y: float64; }
|
||||
func Main() -> int { return 0; }
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
check hir.structs.len == 1
|
||||
check hir.structs[0].name == "Point"
|
||||
check hir.structs[0].fields.len == 2
|
||||
echo " PASS: struct lowering"
|
||||
|
||||
test "method lowering":
|
||||
let src = """
|
||||
struct Point { x: float64; }
|
||||
extend Point {
|
||||
func GetX(self: Point) -> float64 { return 0.0; }
|
||||
}
|
||||
func Main() -> int { return 0; }
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
check hir.funcs.len == 2 # GetX (mangled) + Main
|
||||
var foundMangled = false
|
||||
for f in hir.funcs:
|
||||
if f.name == "Point_GetX":
|
||||
foundMangled = true
|
||||
check foundMangled
|
||||
echo " PASS: method lowering"
|
||||
|
||||
test "if statement":
|
||||
let src = """
|
||||
func Main() -> int {
|
||||
if true { return 1; }
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
let body = hir.funcs[0].body
|
||||
check body.kind == hBlock
|
||||
check body.blockStmts.len >= 1
|
||||
echo " PASS: if statement"
|
||||
|
||||
test "while loop":
|
||||
let src = """
|
||||
func Main() -> int {
|
||||
while true { break; }
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
let body = hir.funcs[0].body
|
||||
check body.kind == hBlock
|
||||
echo " PASS: while loop"
|
||||
|
||||
test "let statement":
|
||||
let src = """
|
||||
func Main() -> int {
|
||||
let x: int = 42;
|
||||
return x;
|
||||
}
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
check hir.funcs.len == 1
|
||||
echo " PASS: let statement"
|
||||
|
||||
test "enum lowering":
|
||||
let src = """
|
||||
enum Color { Red, Green, Blue }
|
||||
func Main() -> int { return 0; }
|
||||
"""
|
||||
let hir = lowerSource(src)
|
||||
check hir.enums.len == 1
|
||||
check hir.enums[0].name == "Color"
|
||||
check hir.enums[0].variants.len == 3
|
||||
echo " PASS: enum lowering"
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,124 @@
|
||||
import std/[unittest, os, strutils]
|
||||
import ../bootstrap/[lexer, token, source_location]
|
||||
|
||||
proc tokenKinds(source: string): seq[TokenKind] =
|
||||
let res = tokenize(source, "<test>")
|
||||
for t in res.tokens:
|
||||
result.add(t.kind)
|
||||
|
||||
proc tokenTexts(source: string): seq[string] =
|
||||
let res = tokenize(source, "<test>")
|
||||
for t in res.tokens:
|
||||
result.add(t.text)
|
||||
|
||||
suite "Lexer":
|
||||
test "empty source":
|
||||
let res = tokenize("", "<test>")
|
||||
check res.tokens.len == 1
|
||||
check res.tokens[0].kind == tkEndOfFile
|
||||
check not res.hasErrors
|
||||
|
||||
test "simple identifiers":
|
||||
let kinds = tokenKinds("foo bar _x Baz123")
|
||||
check kinds == @[tkIdent, tkIdent, tkIdent, tkIdent, tkEndOfFile]
|
||||
|
||||
test "keywords":
|
||||
let kinds = tokenKinds("func let var if else while for return match struct enum")
|
||||
check kinds == @[tkFunc, tkLet, tkVar, tkIf, tkElse, tkWhile, tkFor, tkReturn, tkMatch, tkStruct, tkEnum, tkEndOfFile]
|
||||
|
||||
test "bool literals":
|
||||
let kinds = tokenKinds("true false")
|
||||
check kinds == @[tkBoolLiteral, tkBoolLiteral, tkEndOfFile]
|
||||
let texts = tokenTexts("true false")
|
||||
check texts == @["true", "false", ""]
|
||||
|
||||
test "integers":
|
||||
let kinds = tokenKinds("42 0xFF 0b1010 0o77")
|
||||
check kinds == @[tkIntLiteral, tkIntLiteral, tkIntLiteral, tkIntLiteral, tkEndOfFile]
|
||||
|
||||
test "floats":
|
||||
let kinds = tokenKinds("3.14 1.0e-9 2.5E+3")
|
||||
check kinds == @[tkFloatLiteral, tkFloatLiteral, tkFloatLiteral, tkEndOfFile]
|
||||
|
||||
test "operators":
|
||||
let kinds = tokenKinds("+ - * / % ** ++ --")
|
||||
check kinds == @[tkPlus, tkMinus, tkStar, tkSlash, tkPercent, tkStarStar, tkPlusPlus, tkMinusMinus, tkEndOfFile]
|
||||
|
||||
test "comparison operators":
|
||||
let kinds = tokenKinds("== != < <= > >=")
|
||||
check kinds == @[tkEq, tkNe, tkLt, tkLe, tkGt, tkGe, tkEndOfFile]
|
||||
|
||||
test "assignment operators":
|
||||
let kinds = tokenKinds("= += -= *= /= %= &= |= ^= <<= >>=")
|
||||
check kinds == @[tkAssign, tkPlusAssign, tkMinusAssign, tkStarAssign, tkSlashAssign, tkPercentAssign, tkAmpAssign, tkPipeAssign, tkCaretAssign, tkShlAssign, tkShrAssign, tkEndOfFile]
|
||||
|
||||
test "punctuation":
|
||||
let kinds = tokenKinds("( ) { } [ ] , ; : :: . .. ... ..= -> => @ # ?")
|
||||
check kinds == @[tkLParen, tkRParen, tkLBrace, tkRBrace, tkLBracket, tkRBracket, tkComma, tkSemicolon, tkColon, tkColonColon, tkDot, tkDotDot, tkDotDotDot, tkDotDotEqual, tkArrow, tkFatArrow, tkAt, tkHash, tkQuestion, tkEndOfFile]
|
||||
|
||||
test "logical operators":
|
||||
let kinds = tokenKinds("&& || !")
|
||||
check kinds == @[tkAmpAmp, tkPipePipe, tkBang, tkEndOfFile]
|
||||
|
||||
test "bitwise operators":
|
||||
let kinds = tokenKinds("& | ^ ~ << >>")
|
||||
check kinds == @[tkAmp, tkPipe, tkCaret, tkTilde, tkShl, tkShr, tkEndOfFile]
|
||||
|
||||
test "string literals":
|
||||
let kinds = tokenKinds("\"hello\" c8\"world\"")
|
||||
check kinds == @[tkStringLiteral, tkStringLiteral, tkEndOfFile]
|
||||
|
||||
test "char literals":
|
||||
let kinds = tokenKinds("'A' c8'B'")
|
||||
check kinds == @[tkCharLiteral, tkCharLiteral, tkEndOfFile]
|
||||
|
||||
test "line comments are skipped":
|
||||
let kinds = tokenKinds("let x = 42 // this is a comment")
|
||||
check kinds == @[tkLet, tkIdent, tkAssign, tkIntLiteral, tkEndOfFile]
|
||||
|
||||
test "block comments are skipped":
|
||||
let kinds = tokenKinds("let /* inline comment */ x = 42")
|
||||
check kinds == @[tkLet, tkIdent, tkAssign, tkIntLiteral, tkEndOfFile]
|
||||
|
||||
test "nested block comments":
|
||||
let kinds = tokenKinds("let /* outer /* inner */ still outer */ x = 42")
|
||||
check kinds == @[tkLet, tkIdent, tkAssign, tkIntLiteral, tkEndOfFile]
|
||||
|
||||
test "intrinsics":
|
||||
let kinds = tokenKinds("#line #column #file #function #date #time #module")
|
||||
check kinds == @[tkHashLine, tkHashColumn, tkHashFile, tkHashFunction, tkHashDate, tkHashTime, tkHashModule, tkEndOfFile]
|
||||
|
||||
test "newline tokens":
|
||||
let kinds = tokenKinds("let x = 42\nlet y = 10")
|
||||
check kinds == @[tkLet, tkIdent, tkAssign, tkIntLiteral, tkNewLine, tkLet, tkIdent, tkAssign, tkIntLiteral, tkEndOfFile]
|
||||
|
||||
test "paths":
|
||||
let kinds = tokenKinds("Std::Io::PrintLine")
|
||||
check kinds == @[tkIdent, tkColonColon, tkIdent, tkColonColon, tkIdent, tkEndOfFile]
|
||||
|
||||
test "unterminated string error":
|
||||
let res = tokenize("\"hello", "<test>")
|
||||
check res.hasErrors
|
||||
|
||||
test "unterminated char error":
|
||||
let res = tokenize("'a", "<test>")
|
||||
check res.hasErrors
|
||||
|
||||
test "escape sequences in string":
|
||||
let res = tokenize("\"hello\\nworld\\t!\"", "<test>")
|
||||
check not res.hasErrors
|
||||
check res.tokens[0].kind == tkStringLiteral
|
||||
|
||||
test "full function":
|
||||
let src = "func Main() -> int {\n let x: int32 = 42;\n return x;\n}\n"
|
||||
let res = tokenize(src, "<test>")
|
||||
check not res.hasErrors
|
||||
check res.tokens[0].kind == tkFunc
|
||||
check res.tokens[1].kind == tkIdent
|
||||
check res.tokens[1].text == "Main"
|
||||
|
||||
test "dumpTokens produces output":
|
||||
let res = tokenize("let x = 42", "<test>")
|
||||
let dump = dumpTokens(res)
|
||||
check "let" in dump
|
||||
check "42" in dump
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,122 @@
|
||||
import std/[unittest, os, strutils]
|
||||
import ../bootstrap/[lexer, parser, ast, token]
|
||||
|
||||
proc parseSource(source: string): ParseResult =
|
||||
let lexRes = tokenize(source, "<test>")
|
||||
check not lexRes.hasErrors
|
||||
result = parse(lexRes.tokens, "<test>")
|
||||
|
||||
suite "Parser":
|
||||
test "empty module":
|
||||
let res = parseSource("")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items.len == 0
|
||||
|
||||
test "simple function":
|
||||
let res = parseSource("func Main() -> int { return 0; }")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items.len == 1
|
||||
check res.module.items[0].kind == dkFunc
|
||||
check res.module.items[0].declFuncName == "Main"
|
||||
|
||||
test "function with parameters":
|
||||
let res = parseSource("func Add(a: int32, b: int32) -> int32 { return a + b; }")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].declFuncParams.len == 2
|
||||
check res.module.items[0].declFuncParams[0].name == "a"
|
||||
check res.module.items[0].declFuncParams[1].name == "b"
|
||||
|
||||
test "struct declaration":
|
||||
let res = parseSource("struct Point { x: float64; y: float64; }")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].kind == dkStruct
|
||||
check res.module.items[0].declStructName == "Point"
|
||||
check res.module.items[0].declStructFields.len == 2
|
||||
|
||||
test "enum declaration":
|
||||
let res = parseSource("enum Color { Red, Green, Blue }")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].kind == dkEnum
|
||||
check res.module.items[0].declEnumName == "Color"
|
||||
check res.module.items[0].declEnumVariants.len == 3
|
||||
|
||||
test "import declaration":
|
||||
let res = parseSource("import Std::Io::PrintLine;")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].kind == dkUse
|
||||
check res.module.items[0].declUsePath == @["Std", "Io", "PrintLine"]
|
||||
|
||||
test "const declaration":
|
||||
let res = parseSource("const PI: float64 = 3.14;")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].kind == dkConst
|
||||
check res.module.items[0].declConstName == "PI"
|
||||
|
||||
test "type alias":
|
||||
let res = parseSource("type MyInt = int32;")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].kind == dkTypeAlias
|
||||
check res.module.items[0].declAliasName == "MyInt"
|
||||
|
||||
test "let statement in function":
|
||||
let res = parseSource("func Main() -> int { let x: int32 = 42; return x; }")
|
||||
check res.diagnostics.len == 0
|
||||
check res.module.items[0].declFuncBody.stmts.len == 2
|
||||
check res.module.items[0].declFuncBody.stmts[0].kind == skLet
|
||||
check res.module.items[0].declFuncBody.stmts[0].stmtLetName == "x"
|
||||
|
||||
test "if statement":
|
||||
let res = parseSource("func Main() -> int { if true { return 1; } else { return 0; } }")
|
||||
check res.diagnostics.len == 0
|
||||
let body = res.module.items[0].declFuncBody
|
||||
check body.stmts[0].kind == skIf
|
||||
check body.stmts[0].stmtIfThen.stmts.len == 1
|
||||
check body.stmts[0].stmtIfElse.stmts.len == 1
|
||||
|
||||
test "while loop":
|
||||
let res = parseSource("func Main() -> int { while true { break; } return 0; }")
|
||||
check res.diagnostics.len == 0
|
||||
let body = res.module.items[0].declFuncBody
|
||||
check body.stmts[0].kind == skWhile
|
||||
check body.stmts[0].stmtWhileBody.stmts[0].kind == skBreak
|
||||
|
||||
test "for loop":
|
||||
let res = parseSource("func Main() -> int { for i in items { continue; } return 0; }")
|
||||
check res.diagnostics.len == 0
|
||||
let body = res.module.items[0].declFuncBody
|
||||
check body.stmts[0].kind == skFor
|
||||
check body.stmts[0].stmtForVar == "i"
|
||||
|
||||
test "match expression":
|
||||
let res = parseSource("func Main() -> int { let x = match 1 { 1 => 10, _ => 20 }; return x; }")
|
||||
check res.diagnostics.len == 0
|
||||
let body = res.module.items[0].declFuncBody
|
||||
check body.stmts[0].kind == skLet
|
||||
check body.stmts[0].stmtLetInit.kind == ekMatch
|
||||
|
||||
test "binary expression":
|
||||
let res = parseSource("func Main() -> int { return 1 + 2 * 3; }")
|
||||
check res.diagnostics.len == 0
|
||||
let ret = res.module.items[0].declFuncBody.stmts[0]
|
||||
check ret.kind == skReturn
|
||||
check ret.stmtReturnValue.kind == ekBinary
|
||||
# 1 + (2 * 3)
|
||||
check ret.stmtReturnValue.exprBinaryOp == tkPlus
|
||||
|
||||
test "call expression":
|
||||
let res = parseSource("func Main() -> int { return Add(10, 20); }")
|
||||
check res.diagnostics.len == 0
|
||||
let ret = res.module.items[0].declFuncBody.stmts[0]
|
||||
check ret.stmtReturnValue.kind == ekCall
|
||||
check ret.stmtReturnValue.exprCallCallee.kind == ekIdent
|
||||
|
||||
test "full sample file":
|
||||
let source = readFile(currentSourcePath.parentDir / "testdata" / "sample.bux")
|
||||
let lexRes = tokenize(source, "sample.bux")
|
||||
check not lexRes.hasErrors
|
||||
let parseRes = parse(lexRes.tokens, "sample.bux")
|
||||
check parseRes.diagnostics.len == 0
|
||||
check parseRes.module.items.len == 3
|
||||
check parseRes.module.items[0].kind == dkUse
|
||||
check parseRes.module.items[1].kind == dkFunc
|
||||
check parseRes.module.items[2].kind == dkFunc
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,139 @@
|
||||
import std/[unittest, strutils]
|
||||
import ../bootstrap/[lexer, parser, sema, types]
|
||||
|
||||
proc checkSource(source: string): SemaResult =
|
||||
let lexRes = tokenize(source, "<test>")
|
||||
check not lexRes.hasErrors
|
||||
let parseRes = parse(lexRes.tokens, "<test>")
|
||||
check parseRes.diagnostics.len == 0
|
||||
result = analyze(parseRes.module)
|
||||
|
||||
suite "Sema":
|
||||
test "valid function with correct types":
|
||||
let res = checkSource("func Main() -> int { return 0; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "undeclared identifier":
|
||||
let res = checkSource("func Main() -> int { return x; }")
|
||||
check res.hasErrors
|
||||
check "undeclared" in res.diagnostics[0].message
|
||||
|
||||
test "duplicate function":
|
||||
let res = checkSource("func Main() -> int { return 0; } func Main() -> int { return 1; }")
|
||||
check res.hasErrors
|
||||
check "duplicate" in res.diagnostics[0].message
|
||||
|
||||
test "type mismatch in assignment":
|
||||
let res = checkSource("func Main() -> int { let x: int32 = c8\"hello\"; return 0; }")
|
||||
check res.hasErrors
|
||||
check "cannot assign" in res.diagnostics[0].message
|
||||
|
||||
test "valid arithmetic":
|
||||
let res = checkSource("func Main() -> int { return 1 + 2 * 3; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "arithmetic on strings fails":
|
||||
let res = checkSource("func Main() -> int { return c8\"a\" + c8\"b\"; }")
|
||||
check res.hasErrors
|
||||
|
||||
test "valid function call":
|
||||
let res = checkSource("func Add(a: int, b: int) -> int { return a + b; } func Main() -> int { return Add(1, 2); }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "wrong number of arguments":
|
||||
let res = checkSource("func Add(a: int32, b: int32) -> int32 { return a + b; } func Main() -> int { return Add(1); }")
|
||||
check res.hasErrors
|
||||
check "expected 2 arguments" in res.diagnostics[0].message
|
||||
|
||||
test "wrong argument type":
|
||||
let res = checkSource("func Add(a: int32, b: int32) -> int32 { return a + b; } func Main() -> int { return Add(c8\"a\", 2); }")
|
||||
check res.hasErrors
|
||||
check "argument 1" in res.diagnostics[0].message
|
||||
|
||||
test "if condition must be bool":
|
||||
let res = checkSource("func Main() -> int { if 1 { return 0; } return 0; }")
|
||||
check res.hasErrors
|
||||
check "bool" in res.diagnostics[0].message
|
||||
|
||||
test "valid if with bool":
|
||||
let res = checkSource("func Main() -> int { if true { return 0; } return 0; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "pointer dereference":
|
||||
let res = checkSource("func Main() -> int32 { let p: *int32 = null; return *p; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "struct field access":
|
||||
let res = checkSource("struct Point { x: float64; y: float64; } func Main() -> int32 { let p = Point { x: 1.0, y: 2.0 }; return 0; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "unknown struct field":
|
||||
let res = checkSource("struct Point { x: float64; y: float64; } func Main() -> int32 { let p = Point { x: 1.0, y: 2.0 }; return p.z; }")
|
||||
check res.hasErrors
|
||||
check "no field" in res.diagnostics[0].message
|
||||
|
||||
test "valid comparison":
|
||||
let res = checkSource("func Main() -> int { return 1 == 2; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "valid slice literal":
|
||||
let res = checkSource("func Main() -> int { let arr = [1, 2, 3]; return 0; }")
|
||||
check not res.hasErrors
|
||||
|
||||
test "slice element type mismatch":
|
||||
let res = checkSource("func Main() -> int { let arr = [1, c8\"a\"]; return 0; }")
|
||||
check res.hasErrors
|
||||
|
||||
test "method call with extend":
|
||||
let src = """
|
||||
struct Point { x: float64; y: float64; }
|
||||
extend Point {
|
||||
func Distance(self: Point) -> float64 { return 0.0; }
|
||||
}
|
||||
func Main() -> int {
|
||||
let p = Point { x: 1.0, y: 2.0 };
|
||||
let d = p.Distance();
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
let res = checkSource(src)
|
||||
check not res.hasErrors
|
||||
|
||||
test "method call with wrong arguments":
|
||||
let src = """
|
||||
struct Point { x: float64; y: float64; }
|
||||
extend Point {
|
||||
func Add(self: Point, other: Point) -> Point { return self; }
|
||||
}
|
||||
func Main() -> int {
|
||||
let p = Point { x: 1.0, y: 2.0 };
|
||||
let q = p.Add();
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
let res = checkSource(src)
|
||||
check res.hasErrors
|
||||
check "too few arguments" in res.diagnostics[0].message
|
||||
|
||||
test "interface declaration":
|
||||
let src = """
|
||||
interface Display {
|
||||
func ToString(self: Self) -> String;
|
||||
}
|
||||
"""
|
||||
let res = checkSource(src)
|
||||
check not res.hasErrors
|
||||
|
||||
test "extend for interface":
|
||||
let src = """
|
||||
struct Point { x: float64; y: float64; }
|
||||
interface Display {
|
||||
func ToString(self: Self) -> String;
|
||||
}
|
||||
extend Point for Display {
|
||||
func ToString(self: Point) -> String { return c8"Point"; }
|
||||
}
|
||||
func Main() -> int { return 0; }
|
||||
"""
|
||||
let res = checkSource(src)
|
||||
check not res.hasErrors
|
||||
@@ -0,0 +1,15 @@
|
||||
import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast"
|
||||
|
||||
let source = readFile("../_selfhost/src/ast.bux")
|
||||
let lexRes = tokenize(source, "ast.bux")
|
||||
let res = parse(lexRes.tokens, "ast.bux")
|
||||
if res.module != nil:
|
||||
echo "Module items: ", res.module.items.len
|
||||
for decl in res.module.items:
|
||||
if decl.kind == dkStruct:
|
||||
echo "Struct: ", decl.declStructName, " fields: ", decl.declStructFields.len
|
||||
elif decl.kind == dkModule:
|
||||
echo "Inner module: ", decl.declModuleName, " items: ", decl.declModuleItems.len
|
||||
for inner in decl.declModuleItems:
|
||||
if inner.kind == dkStruct:
|
||||
echo " Struct: ", inner.declStructName, " fields: ", inner.declStructFields.len
|
||||
@@ -0,0 +1,16 @@
|
||||
import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast", "../bootstrap/token"
|
||||
import std/os
|
||||
|
||||
let source = readFile("../_selfhost/src/ast.bux")
|
||||
let lexRes = tokenize(source, "ast.bux")
|
||||
echo "Tokens: ", lexRes.tokens.len
|
||||
echo "Errors: ", lexRes.hasErrors
|
||||
let res = parse(lexRes.tokens, "ast.bux")
|
||||
if res.module == nil:
|
||||
echo "Parse failed, diagnostics: ", res.diagnostics.len
|
||||
for d in res.diagnostics:
|
||||
echo " ", d.message
|
||||
else:
|
||||
echo "Parsed OK, items: ", res.module.items.len
|
||||
for i, decl in res.module.items:
|
||||
echo "Decl ", i, " kind=", decl.kind, " name=", decl.declFuncName
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import Std::Io::PrintLine;
|
||||
|
||||
func Add(a: int32, b: int32) -> int32 {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
let result: int32 = Add(10, 20);
|
||||
PrintLine(c8"Hello from Bux!");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user