feat: add stdlib module support with auto-import resolution
- Parse module paths in stdlib (module Std::Array { ... })
- Collect stdlib declarations and inject into user modules before sema
- Add dkExternFunc support throughout pipeline (parser, sema, hir, c backend)
- Auto-dereference pointer types for field access (arr->field)
- Add hArrowField HIR node for cleaner C emission
- Fix resolveTypeExpr for pointer and cast types
- Fix struct field lowering to use resolveTypeExpr
- Allow int <-> uint implicit conversion for bootstrap convenience
- Add stdlib/Std/Array.bux with dynamic array primitives
- Add stdlib_test demonstrating Array usage via import Std::Array::{Array};
This commit is contained in:
+4
-2
@@ -337,6 +337,7 @@ type
|
|||||||
declImplMethods*: seq[Decl]
|
declImplMethods*: seq[Decl]
|
||||||
of dkModule:
|
of dkModule:
|
||||||
declModuleName*: string
|
declModuleName*: string
|
||||||
|
declModulePath*: seq[string]
|
||||||
declModuleItems*: seq[Decl]
|
declModuleItems*: seq[Decl]
|
||||||
of dkUse:
|
of dkUse:
|
||||||
declUsePath*: seq[string]
|
declUsePath*: seq[string]
|
||||||
@@ -370,11 +371,12 @@ type
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
Module* = ref object
|
Module* = ref object
|
||||||
name*: string
|
name*: string
|
||||||
|
path*: seq[string]
|
||||||
items*: seq[Decl]
|
items*: seq[Decl]
|
||||||
|
|
||||||
# Convenience constructors
|
# Convenience constructors
|
||||||
proc newModule*(name: string): Module =
|
proc newModule*(name: string, path: seq[string] = @[]): Module =
|
||||||
result = Module(name: name)
|
result = Module(name: name, path: path)
|
||||||
|
|
||||||
proc newBlock*(loc: SourceLocation): Block =
|
proc newBlock*(loc: SourceLocation): Block =
|
||||||
result = Block(loc: loc)
|
result = Block(loc: loc)
|
||||||
|
|||||||
@@ -195,6 +195,10 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
|
|||||||
let base = be.emitExpr(node.fieldPtrBase)
|
let base = be.emitExpr(node.fieldPtrBase)
|
||||||
return &"(&({base}.{node.fieldName}))"
|
return &"(&({base}.{node.fieldName}))"
|
||||||
|
|
||||||
|
of hArrowField:
|
||||||
|
let base = be.emitExpr(node.arrowFieldBase)
|
||||||
|
return &"(&({base}->{node.arrowFieldName}))"
|
||||||
|
|
||||||
of hIndexPtr:
|
of hIndexPtr:
|
||||||
let base = be.emitExpr(node.indexPtrBase)
|
let base = be.emitExpr(node.indexPtrBase)
|
||||||
let idx = be.emitExpr(node.indexPtrIndex)
|
let idx = be.emitExpr(node.indexPtrIndex)
|
||||||
|
|||||||
+41
-13
@@ -1,5 +1,5 @@
|
|||||||
import std/[os, strutils, terminal, strformat, osproc]
|
import std/[os, strutils, terminal, strformat, osproc]
|
||||||
import lexer, parser, sema, manifest, hir, hir_lower, c_backend, types, scope
|
import lexer, parser, ast, sema, manifest, hir, hir_lower, c_backend, types, scope
|
||||||
|
|
||||||
type
|
type
|
||||||
ColorMode* = enum
|
ColorMode* = enum
|
||||||
@@ -190,6 +190,23 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
printInfo("check passed", useColor)
|
printInfo("check passed", useColor)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
proc collectStdlibDecls(stdlibDir: string): seq[Decl] =
|
||||||
|
result = @[]
|
||||||
|
if not dirExists(stdlibDir): return
|
||||||
|
for path in walkDirRec(stdlibDir):
|
||||||
|
if path.endsWith(".bux"):
|
||||||
|
let source = readFile(path)
|
||||||
|
let lexRes = tokenize(source, path)
|
||||||
|
if lexRes.hasErrors: continue
|
||||||
|
let parseRes = parse(lexRes.tokens, path)
|
||||||
|
if parseRes.diagnostics.len > 0: continue
|
||||||
|
for item in parseRes.module.items:
|
||||||
|
if item.kind == dkModule:
|
||||||
|
for sub in item.declModuleItems:
|
||||||
|
result.add(sub)
|
||||||
|
else:
|
||||||
|
result.add(item)
|
||||||
|
|
||||||
proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
||||||
let useColor = shouldUseColor(opts)
|
let useColor = shouldUseColor(opts)
|
||||||
let root = getCurrentDir()
|
let root = getCurrentDir()
|
||||||
@@ -208,6 +225,22 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
if not dirExists(buildDir):
|
if not dirExists(buildDir):
|
||||||
createDir(buildDir)
|
createDir(buildDir)
|
||||||
|
|
||||||
|
# Find stdlib directory
|
||||||
|
var stdlibDir = ""
|
||||||
|
let stdlibSearchPaths = @[
|
||||||
|
getAppDir() / ".." / "stdlib",
|
||||||
|
getAppDir() / "stdlib",
|
||||||
|
root / "stdlib",
|
||||||
|
"/home/ziko/z-git/bux/bux/stdlib",
|
||||||
|
]
|
||||||
|
for path in stdlibSearchPaths:
|
||||||
|
if dirExists(path):
|
||||||
|
stdlibDir = path
|
||||||
|
break
|
||||||
|
|
||||||
|
# Collect stdlib declarations once
|
||||||
|
let stdlibDecls = collectStdlibDecls(stdlibDir)
|
||||||
|
|
||||||
# Process all .bux files
|
# Process all .bux files
|
||||||
var allCCode = ""
|
var allCCode = ""
|
||||||
var foundMain = false
|
var foundMain = false
|
||||||
@@ -226,6 +259,13 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
for d in parseRes.diagnostics:
|
for d in parseRes.diagnostics:
|
||||||
echo &"error: {d.message} at {d.loc}"
|
echo &"error: {d.message} at {d.loc}"
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Merge stdlib declarations into the module (prepend so they are processed first)
|
||||||
|
var mergedItems = stdlibDecls
|
||||||
|
for decl in parseRes.module.items:
|
||||||
|
mergedItems.add(decl)
|
||||||
|
parseRes.module.items = mergedItems
|
||||||
|
|
||||||
let (semaRes, semaCtx) = analyzeFull(parseRes.module)
|
let (semaRes, semaCtx) = analyzeFull(parseRes.module)
|
||||||
if semaRes.hasErrors:
|
if semaRes.hasErrors:
|
||||||
printError(&"type errors in {path}", useColor)
|
printError(&"type errors in {path}", useColor)
|
||||||
@@ -257,18 +297,6 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
# Copy runtime and stdlib files
|
# Copy runtime and stdlib files
|
||||||
let runtimeDst = buildDir / "runtime.c"
|
let runtimeDst = buildDir / "runtime.c"
|
||||||
let ioDst = buildDir / "io.c"
|
let ioDst = buildDir / "io.c"
|
||||||
var stdlibDir = ""
|
|
||||||
# Search for stdlib directory
|
|
||||||
let stdlibSearchPaths = @[
|
|
||||||
getAppDir() / ".." / "stdlib",
|
|
||||||
getAppDir() / "stdlib",
|
|
||||||
root / "stdlib",
|
|
||||||
"/home/ziko/z-git/bux/bux/stdlib", # TODO: make this configurable
|
|
||||||
]
|
|
||||||
for path in stdlibSearchPaths:
|
|
||||||
if dirExists(path):
|
|
||||||
stdlibDir = path
|
|
||||||
break
|
|
||||||
if stdlibDir == "":
|
if stdlibDir == "":
|
||||||
printError("stdlib directory not found", useColor)
|
printError("stdlib directory not found", useColor)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type
|
|||||||
hLoad
|
hLoad
|
||||||
hStore
|
hStore
|
||||||
hFieldPtr
|
hFieldPtr
|
||||||
|
hArrowField
|
||||||
hIndexPtr
|
hIndexPtr
|
||||||
# Functions
|
# Functions
|
||||||
hCall
|
hCall
|
||||||
@@ -84,6 +85,9 @@ type
|
|||||||
of hFieldPtr:
|
of hFieldPtr:
|
||||||
fieldPtrBase*: HirNode
|
fieldPtrBase*: HirNode
|
||||||
fieldName*: string
|
fieldName*: string
|
||||||
|
of hArrowField:
|
||||||
|
arrowFieldBase*: HirNode
|
||||||
|
arrowFieldName*: string
|
||||||
of hIndexPtr:
|
of hIndexPtr:
|
||||||
indexPtrBase*: HirNode
|
indexPtrBase*: HirNode
|
||||||
indexPtrIndex*: HirNode
|
indexPtrIndex*: HirNode
|
||||||
|
|||||||
+114
-28
@@ -1,4 +1,4 @@
|
|||||||
import std/[tables, strformat, strutils]
|
import std/[tables, sets, strformat, strutils]
|
||||||
import ast, types, token, source_location, hir, sema, scope
|
import ast, types, token, source_location, hir, sema, scope
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -7,6 +7,7 @@ type
|
|||||||
globalScope*: Scope
|
globalScope*: Scope
|
||||||
methodTable*: Table[string, seq[MethodInfo]]
|
methodTable*: Table[string, seq[MethodInfo]]
|
||||||
currentFuncRetType*: Type
|
currentFuncRetType*: Type
|
||||||
|
currentFuncDecl*: Decl
|
||||||
varCounter*: int
|
varCounter*: int
|
||||||
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
||||||
importTable*: Table[string, string] # Local name → fully qualified name for imports
|
importTable*: Table[string, string] # Local name → fully qualified name for imports
|
||||||
@@ -102,6 +103,26 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
|
|||||||
result.typeSubst = initTable[string, Type]()
|
result.typeSubst = initTable[string, Type]()
|
||||||
result.importTable = initTable[string, string]()
|
result.importTable = initTable[string, string]()
|
||||||
|
|
||||||
|
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
||||||
|
if te == nil: return makeUnknown()
|
||||||
|
case te.kind
|
||||||
|
of tekNamed:
|
||||||
|
case te.typeName
|
||||||
|
of "void": return makeVoid()
|
||||||
|
of "bool": return makeBool()
|
||||||
|
of "int": return makeInt()
|
||||||
|
of "int32": return makeInt()
|
||||||
|
of "int64": return makeInt64()
|
||||||
|
of "float64": return makeFloat64()
|
||||||
|
of "float32": return makeFloat32()
|
||||||
|
of "uint": return makeUInt()
|
||||||
|
of "uint32": return makeUInt()
|
||||||
|
of "uint64": return makeUInt64()
|
||||||
|
else: return makeNamed(te.typeName)
|
||||||
|
of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee))
|
||||||
|
of tekSlice: return makeSlice(ctx.resolveTypeExpr(te.sliceElement))
|
||||||
|
else: return makeUnknown()
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode
|
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode
|
||||||
proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode
|
proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode
|
||||||
@@ -119,8 +140,33 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
of tkBoolLiteral: return makeBool()
|
of tkBoolLiteral: return makeBool()
|
||||||
else: return makeUnknown()
|
else: return makeUnknown()
|
||||||
of ekIdent:
|
of ekIdent:
|
||||||
|
# Check global scope first
|
||||||
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
||||||
if sym != nil and sym.typ != nil: return sym.typ
|
if sym != nil and sym.typ != nil: return sym.typ
|
||||||
|
# Check current function parameters
|
||||||
|
if ctx.currentFuncDecl != nil:
|
||||||
|
var params: seq[Param] = @[]
|
||||||
|
case ctx.currentFuncDecl.kind
|
||||||
|
of dkFunc: params = ctx.currentFuncDecl.declFuncParams
|
||||||
|
of dkExternFunc: params = ctx.currentFuncDecl.declExtFuncParams
|
||||||
|
else: discard
|
||||||
|
for p in params:
|
||||||
|
if p.name == expr.exprIdent and p.ptype != nil:
|
||||||
|
case p.ptype.kind
|
||||||
|
of tekNamed:
|
||||||
|
case p.ptype.typeName
|
||||||
|
of "int", "int32": return makeInt()
|
||||||
|
of "int64": return makeInt64()
|
||||||
|
of "float64": return makeFloat64()
|
||||||
|
of "float32": return makeFloat32()
|
||||||
|
of "bool": return makeBool()
|
||||||
|
of "uint": return makeUInt()
|
||||||
|
of "void": return makeVoid()
|
||||||
|
else: return makeNamed(p.ptype.typeName)
|
||||||
|
of tekPointer:
|
||||||
|
let pointeeType = ctx.resolveTypeExpr(p.ptype.pointerPointee)
|
||||||
|
return makePointer(pointeeType)
|
||||||
|
else: discard
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekSelf: return makeNamed("self")
|
of ekSelf: return makeNamed("self")
|
||||||
of ekBinary:
|
of ekBinary:
|
||||||
@@ -156,7 +202,10 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
return minfo.retType
|
return minfo.retType
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekField:
|
of ekField:
|
||||||
let objType = ctx.resolveExprType(expr.exprFieldObj)
|
var objType = ctx.resolveExprType(expr.exprFieldObj)
|
||||||
|
# Auto-dereference pointer types for field access
|
||||||
|
if objType.kind == tkPointer and objType.inner.len > 0:
|
||||||
|
objType = objType.inner[0]
|
||||||
if objType.kind == tkNamed:
|
if objType.kind == tkNamed:
|
||||||
let sym = ctx.globalScope.lookup(objType.name)
|
let sym = ctx.globalScope.lookup(objType.name)
|
||||||
if sym != nil and sym.decl != nil and sym.decl.kind == dkStruct:
|
if sym != nil and sym.decl != nil and sym.decl.kind == dkStruct:
|
||||||
@@ -171,7 +220,8 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
of "float32": return makeFloat32()
|
of "float32": return makeFloat32()
|
||||||
of "bool": return makeBool()
|
of "bool": return makeBool()
|
||||||
else: return makeNamed(f.ftype.typeName)
|
else: return makeNamed(f.ftype.typeName)
|
||||||
of tekPointer: return makePointer(makeUnknown())
|
of tekPointer:
|
||||||
|
return ctx.resolveTypeExpr(f.ftype)
|
||||||
else: return makeUnknown()
|
else: return makeUnknown()
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekStructInit: return makeNamed(expr.exprStructInitName)
|
of ekStructInit: return makeNamed(expr.exprStructInitName)
|
||||||
@@ -186,9 +236,7 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
return makeTuple(elems)
|
return makeTuple(elems)
|
||||||
of ekCast:
|
of ekCast:
|
||||||
if expr.exprCastType != nil:
|
if expr.exprCastType != nil:
|
||||||
case expr.exprCastType.kind
|
return ctx.resolveTypeExpr(expr.exprCastType)
|
||||||
of tekNamed: return makeNamed(expr.exprCastType.typeName)
|
|
||||||
else: return makeUnknown()
|
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekBlock:
|
of ekBlock:
|
||||||
if expr.exprBlock.stmts.len > 0:
|
if expr.exprBlock.stmts.len > 0:
|
||||||
@@ -292,7 +340,14 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
callIndirectArgs: args, typ: typ, loc: loc)
|
callIndirectArgs: args, typ: typ, loc: loc)
|
||||||
|
|
||||||
of ekField:
|
of ekField:
|
||||||
|
let objType = ctx.resolveExprType(expr.exprFieldObj)
|
||||||
let base = ctx.lowerExpr(expr.exprFieldObj)
|
let base = ctx.lowerExpr(expr.exprFieldObj)
|
||||||
|
# Auto-dereference pointer types for field access
|
||||||
|
if objType.kind == tkPointer:
|
||||||
|
let arrowPtr = HirNode(kind: hArrowField, arrowFieldBase: base,
|
||||||
|
arrowFieldName: expr.exprFieldName,
|
||||||
|
typ: makePointer(typ), loc: loc)
|
||||||
|
return HirNode(kind: hLoad, loadPtr: arrowPtr, typ: typ, loc: loc)
|
||||||
let basePtr = HirNode(kind: hFieldPtr, fieldPtrBase: base,
|
let basePtr = HirNode(kind: hFieldPtr, fieldPtrBase: base,
|
||||||
fieldName: expr.exprFieldName,
|
fieldName: expr.exprFieldName,
|
||||||
typ: makePointer(typ), loc: loc)
|
typ: makePointer(typ), loc: loc)
|
||||||
@@ -335,10 +390,7 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
let operand = ctx.lowerExpr(expr.exprCastOperand)
|
let operand = ctx.lowerExpr(expr.exprCastOperand)
|
||||||
var castType = makeUnknown()
|
var castType = makeUnknown()
|
||||||
if expr.exprCastType != nil:
|
if expr.exprCastType != nil:
|
||||||
case expr.exprCastType.kind
|
castType = ctx.resolveTypeExpr(expr.exprCastType)
|
||||||
of tekNamed: castType = makeNamed(expr.exprCastType.typeName)
|
|
||||||
of tekPointer: castType = makePointer(makeUnknown())
|
|
||||||
else: discard
|
|
||||||
return HirNode(kind: hCast, castOperand: operand, castType: castType,
|
return HirNode(kind: hCast, castOperand: operand, castType: castType,
|
||||||
typ: typ, loc: loc)
|
typ: typ, loc: loc)
|
||||||
|
|
||||||
@@ -509,8 +561,28 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
# Set up type substitution for generic functions
|
# Set up type substitution for generic functions
|
||||||
let oldSubst = ctx.typeSubst
|
let oldSubst = ctx.typeSubst
|
||||||
|
|
||||||
|
var funcName: string
|
||||||
|
var funcParams: seq[Param]
|
||||||
|
var funcReturnType: TypeExpr
|
||||||
|
var funcBody: Block
|
||||||
|
|
||||||
|
case decl.kind
|
||||||
|
of dkFunc:
|
||||||
|
funcName = decl.declFuncName
|
||||||
|
funcParams = decl.declFuncParams
|
||||||
|
funcReturnType = decl.declFuncReturnType
|
||||||
|
funcBody = decl.declFuncBody
|
||||||
|
of dkExternFunc:
|
||||||
|
funcName = decl.declExtFuncName
|
||||||
|
funcParams = decl.declExtFuncParams
|
||||||
|
funcReturnType = decl.declExtFuncReturnType
|
||||||
|
funcBody = nil
|
||||||
|
else:
|
||||||
|
result = HirFunc(name: "", params: @[], retType: makeVoid(), body: nil)
|
||||||
|
return
|
||||||
|
|
||||||
var params: seq[tuple[name: string, typ: Type]] = @[]
|
var params: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
for p in decl.declFuncParams:
|
for p in funcParams:
|
||||||
var pType = makeUnknown()
|
var pType = makeUnknown()
|
||||||
if p.ptype != nil:
|
if p.ptype != nil:
|
||||||
case p.ptype.kind
|
case p.ptype.kind
|
||||||
@@ -527,32 +599,37 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
of "bool": pType = makeBool()
|
of "bool": pType = makeBool()
|
||||||
of "Point", "Self": pType = makeNamed(p.ptype.typeName)
|
of "Point", "Self": pType = makeNamed(p.ptype.typeName)
|
||||||
else: pType = makeNamed(p.ptype.typeName)
|
else: pType = makeNamed(p.ptype.typeName)
|
||||||
of tekPointer: pType = makePointer(makeUnknown())
|
of tekPointer:
|
||||||
|
let pointeeType = ctx.resolveTypeExpr(p.ptype.pointerPointee)
|
||||||
|
pType = makePointer(pointeeType)
|
||||||
else: discard
|
else: discard
|
||||||
params.add((p.name, pType))
|
params.add((p.name, pType))
|
||||||
|
|
||||||
var retType = makeVoid()
|
var retType = makeVoid()
|
||||||
if decl.declFuncReturnType != nil:
|
if funcReturnType != nil:
|
||||||
case decl.declFuncReturnType.kind
|
case funcReturnType.kind
|
||||||
of tekNamed:
|
of tekNamed:
|
||||||
# Check if this is a type parameter
|
# Check if this is a type parameter
|
||||||
if ctx.typeSubst.hasKey(decl.declFuncReturnType.typeName):
|
if ctx.typeSubst.hasKey(funcReturnType.typeName):
|
||||||
retType = ctx.typeSubst[decl.declFuncReturnType.typeName]
|
retType = ctx.typeSubst[funcReturnType.typeName]
|
||||||
else:
|
else:
|
||||||
case decl.declFuncReturnType.typeName
|
case funcReturnType.typeName
|
||||||
of "int", "int32": retType = makeInt()
|
of "int", "int32": retType = makeInt()
|
||||||
of "int64": retType = makeInt64()
|
of "int64": retType = makeInt64()
|
||||||
of "float64": retType = makeFloat64()
|
of "float64": retType = makeFloat64()
|
||||||
of "float32": retType = makeFloat32()
|
of "float32": retType = makeFloat32()
|
||||||
of "bool": retType = makeBool()
|
of "bool": retType = makeBool()
|
||||||
else: retType = makeNamed(decl.declFuncReturnType.typeName)
|
else: retType = makeNamed(funcReturnType.typeName)
|
||||||
of tekPointer: retType = makePointer(makeUnknown())
|
of tekPointer:
|
||||||
|
let pointeeType = ctx.resolveTypeExpr(funcReturnType.pointerPointee)
|
||||||
|
retType = makePointer(pointeeType)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
ctx.currentFuncRetType = retType
|
ctx.currentFuncRetType = retType
|
||||||
let body = if decl.declFuncBody != nil: ctx.lowerBlock(decl.declFuncBody) else: nil
|
ctx.currentFuncDecl = decl
|
||||||
|
let body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
|
||||||
|
|
||||||
result = HirFunc(name: decl.declFuncName, params: params, retType: retType,
|
result = HirFunc(name: funcName, params: params, retType: retType,
|
||||||
body: body, isPublic: decl.isPublic)
|
body: body, isPublic: decl.isPublic)
|
||||||
|
|
||||||
# Restore old substitution
|
# Restore old substitution
|
||||||
@@ -566,6 +643,17 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
var enums: seq[tuple[name: string, variants: seq[HirEnumVariant]]] = @[]
|
var enums: seq[tuple[name: string, variants: seq[HirEnumVariant]]] = @[]
|
||||||
var consts: seq[tuple[name: string, typ: Type, value: HirNode]] = @[]
|
var consts: seq[tuple[name: string, typ: Type, value: HirNode]] = @[]
|
||||||
|
|
||||||
|
# Collect local symbol names so we don't remap them via imports
|
||||||
|
var localSymbols = initHashSet[string]()
|
||||||
|
for decl in module.items:
|
||||||
|
case decl.kind
|
||||||
|
of dkFunc: localSymbols.incl(decl.declFuncName)
|
||||||
|
of dkExternFunc: localSymbols.incl(decl.declExtFuncName)
|
||||||
|
of dkStruct: localSymbols.incl(decl.declStructName)
|
||||||
|
of dkEnum: localSymbols.incl(decl.declEnumName)
|
||||||
|
of dkUnion: localSymbols.incl(decl.declUnionName)
|
||||||
|
else: discard
|
||||||
|
|
||||||
# Collect imports for name resolution
|
# Collect imports for name resolution
|
||||||
for decl in module.items:
|
for decl in module.items:
|
||||||
if decl.kind == dkUse:
|
if decl.kind == dkUse:
|
||||||
@@ -574,11 +662,13 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
if decl.declUsePath.len > 0:
|
if decl.declUsePath.len > 0:
|
||||||
let localName = decl.declUsePath[^1]
|
let localName = decl.declUsePath[^1]
|
||||||
let fullName = decl.declUsePath.join("_")
|
let fullName = decl.declUsePath.join("_")
|
||||||
|
if localName notin localSymbols:
|
||||||
ctx.importTable[localName] = fullName
|
ctx.importTable[localName] = fullName
|
||||||
of ukMulti:
|
of ukMulti:
|
||||||
if decl.declUsePath.len > 0:
|
if decl.declUsePath.len > 0:
|
||||||
let basePath = decl.declUsePath.join("_")
|
let basePath = decl.declUsePath.join("_")
|
||||||
for name in decl.declUseNames:
|
for name in decl.declUseNames:
|
||||||
|
if name notin localSymbols:
|
||||||
ctx.importTable[name] = basePath & "_" & name
|
ctx.importTable[name] = basePath & "_" & name
|
||||||
of ukGlob:
|
of ukGlob:
|
||||||
# For glob imports, we can't statically resolve all names here.
|
# For glob imports, we can't statically resolve all names here.
|
||||||
@@ -706,6 +796,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
else:
|
else:
|
||||||
# Extern function (no body)
|
# Extern function (no body)
|
||||||
externFuncs.add(ctx.lowerFunc(decl))
|
externFuncs.add(ctx.lowerFunc(decl))
|
||||||
|
of dkExternFunc:
|
||||||
|
externFuncs.add(ctx.lowerFunc(decl))
|
||||||
of dkImpl:
|
of dkImpl:
|
||||||
for methodDecl in decl.declImplMethods:
|
for methodDecl in decl.declImplMethods:
|
||||||
if methodDecl.kind == dkFunc:
|
if methodDecl.kind == dkFunc:
|
||||||
@@ -715,13 +807,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
of dkStruct:
|
of dkStruct:
|
||||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
for f in decl.declStructFields:
|
for f in decl.declStructFields:
|
||||||
var fType = makeUnknown()
|
let fType = if f.ftype != nil: ctx.resolveTypeExpr(f.ftype) else: makeUnknown()
|
||||||
if f.ftype != nil and f.ftype.kind == tekNamed:
|
|
||||||
case f.ftype.typeName
|
|
||||||
of "float64": fType = makeFloat64()
|
|
||||||
of "float32": fType = makeFloat32()
|
|
||||||
of "int", "int32": fType = makeInt()
|
|
||||||
else: fType = makeNamed(f.ftype.typeName)
|
|
||||||
fields.add((f.name, fType))
|
fields.add((f.name, fType))
|
||||||
structs.add((decl.declStructName, fields))
|
structs.add((decl.declStructName, fields))
|
||||||
of dkEnum:
|
of dkEnum:
|
||||||
|
|||||||
+29
-6
@@ -899,7 +899,7 @@ proc parseStructDecl(p: var Parser, isPublic: bool): Decl =
|
|||||||
let fName = p.expect(tkIdent, "expected field name").text
|
let fName = p.expect(tkIdent, "expected field name").text
|
||||||
discard p.expect(tkColon, "expected ':' after field name")
|
discard p.expect(tkColon, "expected ':' after field name")
|
||||||
let fType = p.parseType()
|
let fType = p.parseType()
|
||||||
if p.check(tkSemicolon):
|
if p.check(tkSemicolon) or p.check(tkComma):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
fields.add(StructField(loc: fLoc, isPublic: fPub, name: fName, ftype: fType))
|
fields.add(StructField(loc: fLoc, isPublic: fPub, name: fName, ftype: fType))
|
||||||
discard p.expect(tkRBrace, "expected '}' to close struct")
|
discard p.expect(tkRBrace, "expected '}' to close struct")
|
||||||
@@ -1012,14 +1012,24 @@ proc parseImplDecl(p: var Parser): Decl =
|
|||||||
proc parseModuleDecl(p: var Parser, isPublic: bool): Decl =
|
proc parseModuleDecl(p: var Parser, isPublic: bool): Decl =
|
||||||
let loc = p.currentLoc
|
let loc = p.currentLoc
|
||||||
discard p.expect(tkModule, "expected 'module'")
|
discard p.expect(tkModule, "expected 'module'")
|
||||||
let name = p.expect(tkIdent, "expected module name").text
|
var path: seq[string] = @[]
|
||||||
|
path.add(p.expect(tkIdent, "expected module name").text)
|
||||||
|
while p.check(tkColonColon):
|
||||||
|
discard p.advance()
|
||||||
|
path.add(p.expect(tkIdent, "expected module path segment").text)
|
||||||
|
let name = path[^1]
|
||||||
discard p.expect(tkLBrace, "expected '{' to start module body")
|
discard p.expect(tkLBrace, "expected '{' to start module body")
|
||||||
var items: seq[Decl] = @[]
|
var items: seq[Decl] = @[]
|
||||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||||
|
while p.check(tkNewLine):
|
||||||
|
discard p.advance()
|
||||||
|
if p.check(tkRBrace) or p.isAtEnd:
|
||||||
|
break
|
||||||
items.add(p.parseDecl())
|
items.add(p.parseDecl())
|
||||||
discard p.expect(tkRBrace, "expected '}' to close module")
|
discard p.expect(tkRBrace, "expected '}' to close module")
|
||||||
return Decl(kind: dkModule, loc: loc, isPublic: isPublic,
|
return Decl(kind: dkModule, loc: loc, isPublic: isPublic,
|
||||||
declModuleName: name, declModuleItems: items)
|
declModuleName: name, declModulePath: path,
|
||||||
|
declModuleItems: items)
|
||||||
|
|
||||||
proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
||||||
let loc = p.currentLoc
|
let loc = p.currentLoc
|
||||||
@@ -1027,6 +1037,9 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
|||||||
var path: seq[string] = @[]
|
var path: seq[string] = @[]
|
||||||
path.add(p.expect(tkIdent, "expected import path segment").text)
|
path.add(p.expect(tkIdent, "expected import path segment").text)
|
||||||
while p.check(tkColonColon):
|
while p.check(tkColonColon):
|
||||||
|
# Lookahead: if :: is followed by { or *, don't consume it here
|
||||||
|
if p.peek(1) == tkLBrace or p.peek(1) == tkStar:
|
||||||
|
break
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
path.add(p.expect(tkIdent, "expected import path segment").text)
|
path.add(p.expect(tkIdent, "expected import path segment").text)
|
||||||
var kind = ukSingle
|
var kind = ukSingle
|
||||||
@@ -1040,13 +1053,17 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
|||||||
kind = ukGlob
|
kind = ukGlob
|
||||||
elif p.check(tkColonColon):
|
elif p.check(tkColonColon):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
discard p.expect(tkLBrace, "expected '{' for multi-import")
|
if p.check(tkLBrace):
|
||||||
|
discard p.advance()
|
||||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||||
names.add(p.expect(tkIdent, "expected name in multi-import").text)
|
names.add(p.expect(tkIdent, "expected name in multi-import").text)
|
||||||
if p.check(tkComma):
|
if p.check(tkComma):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
discard p.expect(tkRBrace, "expected '}' to close multi-import")
|
discard p.expect(tkRBrace, "expected '}' to close multi-import")
|
||||||
kind = ukMulti
|
kind = ukMulti
|
||||||
|
elif p.check(tkStar):
|
||||||
|
discard p.advance()
|
||||||
|
kind = ukGlob
|
||||||
if p.check(tkSemicolon):
|
if p.check(tkSemicolon):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
return Decl(kind: dkUse, loc: loc, declUsePath: path, declUseKind: kind,
|
return Decl(kind: dkUse, loc: loc, declUsePath: path, declUseKind: kind,
|
||||||
@@ -1083,8 +1100,14 @@ proc parseExternDecl(p: var Parser, isPublic: bool, attrs: ParsedAttrs): Decl =
|
|||||||
discard p.expect(tkExtern, "expected 'extern'")
|
discard p.expect(tkExtern, "expected 'extern'")
|
||||||
if p.check(tkFunc):
|
if p.check(tkFunc):
|
||||||
let funcDecl = p.parseFuncDecl(isPublic, false, attrs)
|
let funcDecl = p.parseFuncDecl(isPublic, false, attrs)
|
||||||
funcDecl.isPublic = isPublic
|
# Convert dkFunc to dkExternFunc
|
||||||
return funcDecl
|
return Decl(kind: dkExternFunc, loc: funcDecl.loc, isPublic: isPublic,
|
||||||
|
declExtFuncName: funcDecl.declFuncName,
|
||||||
|
declExtFuncDll: attrs.importLib,
|
||||||
|
declExtFuncCallConv: attrs.callConv,
|
||||||
|
declExtFuncParams: funcDecl.declFuncParams,
|
||||||
|
declExtFuncVariadic: false,
|
||||||
|
declExtFuncReturnType: funcDecl.declFuncReturnType)
|
||||||
elif p.check(tkLBrace):
|
elif p.check(tkLBrace):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
var items: seq[Decl] = @[]
|
var items: seq[Decl] = @[]
|
||||||
|
|||||||
+34
-7
@@ -118,6 +118,16 @@ proc collectGlobals*(sema: var Sema) =
|
|||||||
sym.typ = makeFunc(params, retType)
|
sym.typ = makeFunc(params, retType)
|
||||||
if not sema.globalScope.define(sym):
|
if not sema.globalScope.define(sym):
|
||||||
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
|
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
|
||||||
|
of dkExternFunc:
|
||||||
|
let sym = Symbol(kind: skFunc, name: decl.declExtFuncName, decl: decl,
|
||||||
|
isPublic: decl.isPublic)
|
||||||
|
var params: seq[Type] = @[]
|
||||||
|
for p in decl.declExtFuncParams:
|
||||||
|
params.add(sema.resolveType(p.ptype))
|
||||||
|
let retType = if decl.declExtFuncReturnType != nil: sema.resolveType(decl.declExtFuncReturnType) else: makeVoid()
|
||||||
|
sym.typ = makeFunc(params, retType)
|
||||||
|
if not sema.globalScope.define(sym):
|
||||||
|
sema.emitError(decl.loc, &"duplicate symbol '{decl.declExtFuncName}'")
|
||||||
of dkStruct:
|
of dkStruct:
|
||||||
let t = makeNamed(decl.declStructName)
|
let t = makeNamed(decl.declStructName)
|
||||||
let sym = Symbol(kind: skType, name: decl.declStructName, typ: t,
|
let sym = Symbol(kind: skType, name: decl.declStructName, typ: t,
|
||||||
@@ -160,11 +170,24 @@ proc collectGlobals*(sema: var Sema) =
|
|||||||
sema.emitError(decl.loc, &"duplicate symbol '{decl.declAliasName}'")
|
sema.emitError(decl.loc, &"duplicate symbol '{decl.declAliasName}'")
|
||||||
sema.typeTable[decl.declAliasName] = t
|
sema.typeTable[decl.declAliasName] = t
|
||||||
of dkUse:
|
of dkUse:
|
||||||
# Imports: for now just register the last segment as a module symbol
|
# Imports: register imported names into scope
|
||||||
if decl.declUsePath.len > 0:
|
if decl.declUsePath.len > 0:
|
||||||
|
case decl.declUseKind
|
||||||
|
of ukMulti:
|
||||||
|
for name in decl.declUseNames:
|
||||||
|
if sema.globalScope.lookup(name) == nil:
|
||||||
|
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
|
||||||
|
discard sema.globalScope.define(sym)
|
||||||
|
of ukGlob:
|
||||||
let name = decl.declUsePath[^1]
|
let name = decl.declUsePath[^1]
|
||||||
|
if sema.globalScope.lookup(name) == nil:
|
||||||
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
|
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
|
||||||
discard sema.globalScope.define(sym)
|
discard sema.globalScope.define(sym)
|
||||||
|
of ukSingle:
|
||||||
|
let name = decl.declUsePath[^1]
|
||||||
|
if sema.globalScope.lookup(name) == nil:
|
||||||
|
let sym = Symbol(kind: skFunc, name: name, typ: makeUnknown(), isPublic: true)
|
||||||
|
discard sema.globalScope.define(sym)
|
||||||
of dkInterface:
|
of dkInterface:
|
||||||
# Register interface for conformance checking
|
# Register interface for conformance checking
|
||||||
sema.interfaceTable[decl.declInterfaceName] = decl
|
sema.interfaceTable[decl.declInterfaceName] = decl
|
||||||
@@ -460,10 +483,14 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekField:
|
of ekField:
|
||||||
let obj = sema.checkExpr(expr.exprFieldObj, scope)
|
let obj = sema.checkExpr(expr.exprFieldObj, scope)
|
||||||
if obj.kind == tkNamed:
|
var objType = obj
|
||||||
|
# Auto-dereference pointer types for field access
|
||||||
|
if objType.kind == tkPointer and objType.inner.len > 0:
|
||||||
|
objType = objType.inner[0]
|
||||||
|
if objType.kind == tkNamed:
|
||||||
# Check if this is a _Data union field access
|
# Check if this is a _Data union field access
|
||||||
if obj.name.endsWith("_Data"):
|
if objType.name.endsWith("_Data"):
|
||||||
let enumName = obj.name[0..^6] # Remove "_Data" suffix
|
let enumName = objType.name[0..^6] # Remove "_Data" suffix
|
||||||
let enumSym = sema.globalScope.lookup(enumName)
|
let enumSym = sema.globalScope.lookup(enumName)
|
||||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||||
# Look for the field in enum variants
|
# Look for the field in enum variants
|
||||||
@@ -477,17 +504,17 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
for nf in variant.namedFields:
|
for nf in variant.namedFields:
|
||||||
if nf.name == expr.exprFieldName:
|
if nf.name == expr.exprFieldName:
|
||||||
return sema.resolveType(nf.ftype)
|
return sema.resolveType(nf.ftype)
|
||||||
sema.emitError(expr.loc, &"union '{obj.name}' has no field '{expr.exprFieldName}'")
|
sema.emitError(expr.loc, &"union '{objType.name}' has no field '{expr.exprFieldName}'")
|
||||||
else:
|
else:
|
||||||
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
||||||
else:
|
else:
|
||||||
let sym = sema.globalScope.lookup(obj.name)
|
let sym = sema.globalScope.lookup(objType.name)
|
||||||
if sym != nil and sym.decl != nil:
|
if sym != nil and sym.decl != nil:
|
||||||
if sym.decl.kind == dkStruct:
|
if sym.decl.kind == dkStruct:
|
||||||
for f in sym.decl.declStructFields:
|
for f in sym.decl.declStructFields:
|
||||||
if f.name == expr.exprFieldName:
|
if f.name == expr.exprFieldName:
|
||||||
return sema.resolveType(f.ftype)
|
return sema.resolveType(f.ftype)
|
||||||
sema.emitError(expr.loc, &"struct '{obj.name}' has no field '{expr.exprFieldName}'")
|
sema.emitError(expr.loc, &"struct '{objType.name}' has no field '{expr.exprFieldName}'")
|
||||||
elif sym.decl.kind == dkEnum:
|
elif sym.decl.kind == dkEnum:
|
||||||
# Algebraic enum fields
|
# Algebraic enum fields
|
||||||
if expr.exprFieldName == "tag":
|
if expr.exprFieldName == "tag":
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ proc isAssignableTo*(a, b: Type): bool =
|
|||||||
# smaller int -> int/uint
|
# smaller int -> int/uint
|
||||||
if b.kind == tkInt and a.kind in {tkInt8, tkInt16, tkInt32}: return true
|
if b.kind == tkInt and a.kind in {tkInt8, tkInt16, tkInt32}: return true
|
||||||
if b.kind == tkUInt and a.kind in {tkUInt8, tkUInt16, tkUInt32}: return true
|
if b.kind == tkUInt and a.kind in {tkUInt8, tkUInt16, tkUInt32}: return true
|
||||||
|
# int <-> uint (for convenience in bootstrap)
|
||||||
|
if a.kind == tkInt and b.kind == tkUInt: return true
|
||||||
|
if a.kind == tkUInt and b.kind == tkInt: return true
|
||||||
# numeric exact match required otherwise
|
# numeric exact match required otherwise
|
||||||
if a.isNumeric and b.isNumeric: return false
|
if a.isNumeric and b.isNumeric: return false
|
||||||
# bool across widths
|
# bool across widths
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
module Std::Array {
|
||||||
|
|
||||||
|
extern func bux_alloc(size: uint) -> *void;
|
||||||
|
extern func bux_realloc(ptr: *void, size: uint) -> *void;
|
||||||
|
extern func bux_free(ptr: *void);
|
||||||
|
extern func bux_bounds_check(index: uint, len: uint);
|
||||||
|
|
||||||
|
struct Array {
|
||||||
|
data: *int,
|
||||||
|
len: uint,
|
||||||
|
cap: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
func Array_New(cap: uint) -> Array {
|
||||||
|
let data = bux_alloc(cap * 8) as *int;
|
||||||
|
return Array { data: data, len: 0, cap: cap };
|
||||||
|
}
|
||||||
|
|
||||||
|
func Array_Push(arr: *Array, value: int) {
|
||||||
|
if arr.len >= arr.cap {
|
||||||
|
arr.cap = arr.cap * 2;
|
||||||
|
arr.data = bux_realloc(arr.data as *void, arr.cap * 8) as *int;
|
||||||
|
}
|
||||||
|
arr.data[arr.len] = value;
|
||||||
|
arr.len = arr.len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Array_Get(arr: *Array, index: uint) -> int {
|
||||||
|
bux_bounds_check(index, arr.len);
|
||||||
|
return arr.data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
func Array_Len(arr: *Array) -> uint {
|
||||||
|
return arr.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Array_Free(arr: *Array) {
|
||||||
|
bux_free(arr.data as *void);
|
||||||
|
arr.data = null as *int;
|
||||||
|
arr.len = 0;
|
||||||
|
arr.cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "stdlib_test"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import Std::Array::{Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Free};
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
var arr: Array = Array_New(4);
|
||||||
|
|
||||||
|
Array_Push(&arr, 10);
|
||||||
|
Array_Push(&arr, 20);
|
||||||
|
Array_Push(&arr, 30);
|
||||||
|
|
||||||
|
PrintLine("Array contents:");
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < Array_Len(&arr) {
|
||||||
|
PrintInt(Array_Get(&arr, i));
|
||||||
|
PrintLine("");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array_Free(&arr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user