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]
|
||||
of dkModule:
|
||||
declModuleName*: string
|
||||
declModulePath*: seq[string]
|
||||
declModuleItems*: seq[Decl]
|
||||
of dkUse:
|
||||
declUsePath*: seq[string]
|
||||
@@ -370,11 +371,12 @@ type
|
||||
# ---------------------------------------------------------------------------
|
||||
Module* = ref object
|
||||
name*: string
|
||||
path*: seq[string]
|
||||
items*: seq[Decl]
|
||||
|
||||
# Convenience constructors
|
||||
proc newModule*(name: string): Module =
|
||||
result = Module(name: name)
|
||||
proc newModule*(name: string, path: seq[string] = @[]): Module =
|
||||
result = Module(name: name, path: path)
|
||||
|
||||
proc newBlock*(loc: SourceLocation): Block =
|
||||
result = Block(loc: loc)
|
||||
|
||||
@@ -195,6 +195,10 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
|
||||
let base = be.emitExpr(node.fieldPtrBase)
|
||||
return &"(&({base}.{node.fieldName}))"
|
||||
|
||||
of hArrowField:
|
||||
let base = be.emitExpr(node.arrowFieldBase)
|
||||
return &"(&({base}->{node.arrowFieldName}))"
|
||||
|
||||
of hIndexPtr:
|
||||
let base = be.emitExpr(node.indexPtrBase)
|
||||
let idx = be.emitExpr(node.indexPtrIndex)
|
||||
|
||||
+41
-13
@@ -1,5 +1,5 @@
|
||||
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
|
||||
ColorMode* = enum
|
||||
@@ -190,6 +190,23 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
|
||||
printInfo("check passed", useColor)
|
||||
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 =
|
||||
let useColor = shouldUseColor(opts)
|
||||
let root = getCurrentDir()
|
||||
@@ -208,6 +225,22 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
||||
if not dirExists(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
|
||||
var allCCode = ""
|
||||
var foundMain = false
|
||||
@@ -226,6 +259,13 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
||||
for d in parseRes.diagnostics:
|
||||
echo &"error: {d.message} at {d.loc}"
|
||||
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)
|
||||
if semaRes.hasErrors:
|
||||
printError(&"type errors in {path}", useColor)
|
||||
@@ -257,18 +297,6 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
||||
# Copy runtime and stdlib files
|
||||
let runtimeDst = buildDir / "runtime.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 == "":
|
||||
printError("stdlib directory not found", useColor)
|
||||
return 1
|
||||
|
||||
@@ -22,6 +22,7 @@ type
|
||||
hLoad
|
||||
hStore
|
||||
hFieldPtr
|
||||
hArrowField
|
||||
hIndexPtr
|
||||
# Functions
|
||||
hCall
|
||||
@@ -84,6 +85,9 @@ type
|
||||
of hFieldPtr:
|
||||
fieldPtrBase*: HirNode
|
||||
fieldName*: string
|
||||
of hArrowField:
|
||||
arrowFieldBase*: HirNode
|
||||
arrowFieldName*: string
|
||||
of hIndexPtr:
|
||||
indexPtrBase*: HirNode
|
||||
indexPtrIndex*: HirNode
|
||||
|
||||
+116
-30
@@ -1,4 +1,4 @@
|
||||
import std/[tables, strformat, strutils]
|
||||
import std/[tables, sets, strformat, strutils]
|
||||
import ast, types, token, source_location, hir, sema, scope
|
||||
|
||||
type
|
||||
@@ -7,6 +7,7 @@ type
|
||||
globalScope*: Scope
|
||||
methodTable*: Table[string, seq[MethodInfo]]
|
||||
currentFuncRetType*: Type
|
||||
currentFuncDecl*: Decl
|
||||
varCounter*: int
|
||||
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
||||
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.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
|
||||
proc lowerExpr(ctx: var LowerCtx, expr: Expr): 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()
|
||||
else: return makeUnknown()
|
||||
of ekIdent:
|
||||
# Check global scope first
|
||||
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
||||
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()
|
||||
of ekSelf: return makeNamed("self")
|
||||
of ekBinary:
|
||||
@@ -156,7 +202,10 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
return minfo.retType
|
||||
return makeUnknown()
|
||||
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:
|
||||
let sym = ctx.globalScope.lookup(objType.name)
|
||||
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 "bool": return makeBool()
|
||||
else: return makeNamed(f.ftype.typeName)
|
||||
of tekPointer: return makePointer(makeUnknown())
|
||||
of tekPointer:
|
||||
return ctx.resolveTypeExpr(f.ftype)
|
||||
else: return makeUnknown()
|
||||
return makeUnknown()
|
||||
of ekStructInit: return makeNamed(expr.exprStructInitName)
|
||||
@@ -186,9 +236,7 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
return makeTuple(elems)
|
||||
of ekCast:
|
||||
if expr.exprCastType != nil:
|
||||
case expr.exprCastType.kind
|
||||
of tekNamed: return makeNamed(expr.exprCastType.typeName)
|
||||
else: return makeUnknown()
|
||||
return ctx.resolveTypeExpr(expr.exprCastType)
|
||||
return makeUnknown()
|
||||
of ekBlock:
|
||||
if expr.exprBlock.stmts.len > 0:
|
||||
@@ -292,7 +340,14 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||
callIndirectArgs: args, typ: typ, loc: loc)
|
||||
|
||||
of ekField:
|
||||
let objType = ctx.resolveExprType(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,
|
||||
fieldName: expr.exprFieldName,
|
||||
typ: makePointer(typ), loc: loc)
|
||||
@@ -335,10 +390,7 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||
let operand = ctx.lowerExpr(expr.exprCastOperand)
|
||||
var castType = makeUnknown()
|
||||
if expr.exprCastType != nil:
|
||||
case expr.exprCastType.kind
|
||||
of tekNamed: castType = makeNamed(expr.exprCastType.typeName)
|
||||
of tekPointer: castType = makePointer(makeUnknown())
|
||||
else: discard
|
||||
castType = ctx.resolveTypeExpr(expr.exprCastType)
|
||||
return HirNode(kind: hCast, castOperand: operand, castType: castType,
|
||||
typ: typ, loc: loc)
|
||||
|
||||
@@ -509,8 +561,28 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
# Set up type substitution for generic functions
|
||||
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]] = @[]
|
||||
for p in decl.declFuncParams:
|
||||
for p in funcParams:
|
||||
var pType = makeUnknown()
|
||||
if p.ptype != nil:
|
||||
case p.ptype.kind
|
||||
@@ -527,32 +599,37 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
of "bool": pType = makeBool()
|
||||
of "Point", "Self": 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
|
||||
params.add((p.name, pType))
|
||||
|
||||
var retType = makeVoid()
|
||||
if decl.declFuncReturnType != nil:
|
||||
case decl.declFuncReturnType.kind
|
||||
if funcReturnType != nil:
|
||||
case funcReturnType.kind
|
||||
of tekNamed:
|
||||
# Check if this is a type parameter
|
||||
if ctx.typeSubst.hasKey(decl.declFuncReturnType.typeName):
|
||||
retType = ctx.typeSubst[decl.declFuncReturnType.typeName]
|
||||
if ctx.typeSubst.hasKey(funcReturnType.typeName):
|
||||
retType = ctx.typeSubst[funcReturnType.typeName]
|
||||
else:
|
||||
case decl.declFuncReturnType.typeName
|
||||
case funcReturnType.typeName
|
||||
of "int", "int32": retType = makeInt()
|
||||
of "int64": retType = makeInt64()
|
||||
of "float64": retType = makeFloat64()
|
||||
of "float32": retType = makeFloat32()
|
||||
of "bool": retType = makeBool()
|
||||
else: retType = makeNamed(decl.declFuncReturnType.typeName)
|
||||
of tekPointer: retType = makePointer(makeUnknown())
|
||||
else: retType = makeNamed(funcReturnType.typeName)
|
||||
of tekPointer:
|
||||
let pointeeType = ctx.resolveTypeExpr(funcReturnType.pointerPointee)
|
||||
retType = makePointer(pointeeType)
|
||||
else: discard
|
||||
|
||||
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)
|
||||
|
||||
# Restore old substitution
|
||||
@@ -566,6 +643,17 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
var enums: seq[tuple[name: string, variants: seq[HirEnumVariant]]] = @[]
|
||||
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
|
||||
for decl in module.items:
|
||||
if decl.kind == dkUse:
|
||||
@@ -574,12 +662,14 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
if decl.declUsePath.len > 0:
|
||||
let localName = decl.declUsePath[^1]
|
||||
let fullName = decl.declUsePath.join("_")
|
||||
ctx.importTable[localName] = fullName
|
||||
if localName notin localSymbols:
|
||||
ctx.importTable[localName] = fullName
|
||||
of ukMulti:
|
||||
if decl.declUsePath.len > 0:
|
||||
let basePath = decl.declUsePath.join("_")
|
||||
for name in decl.declUseNames:
|
||||
ctx.importTable[name] = basePath & "_" & name
|
||||
if name notin localSymbols:
|
||||
ctx.importTable[name] = basePath & "_" & name
|
||||
of ukGlob:
|
||||
# For glob imports, we can't statically resolve all names here.
|
||||
# Store the base path for potential future use.
|
||||
@@ -706,6 +796,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
else:
|
||||
# Extern function (no body)
|
||||
externFuncs.add(ctx.lowerFunc(decl))
|
||||
of dkExternFunc:
|
||||
externFuncs.add(ctx.lowerFunc(decl))
|
||||
of dkImpl:
|
||||
for methodDecl in decl.declImplMethods:
|
||||
if methodDecl.kind == dkFunc:
|
||||
@@ -715,13 +807,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
of dkStruct:
|
||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||
for f in decl.declStructFields:
|
||||
var fType = 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)
|
||||
let fType = if f.ftype != nil: ctx.resolveTypeExpr(f.ftype) else: makeUnknown()
|
||||
fields.add((f.name, fType))
|
||||
structs.add((decl.declStructName, fields))
|
||||
of dkEnum:
|
||||
|
||||
+35
-12
@@ -899,7 +899,7 @@ proc parseStructDecl(p: var Parser, isPublic: bool): Decl =
|
||||
let fName = p.expect(tkIdent, "expected field name").text
|
||||
discard p.expect(tkColon, "expected ':' after field name")
|
||||
let fType = p.parseType()
|
||||
if p.check(tkSemicolon):
|
||||
if p.check(tkSemicolon) or p.check(tkComma):
|
||||
discard p.advance()
|
||||
fields.add(StructField(loc: fLoc, isPublic: fPub, name: fName, ftype: fType))
|
||||
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 =
|
||||
let loc = p.currentLoc
|
||||
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")
|
||||
var items: seq[Decl] = @[]
|
||||
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())
|
||||
discard p.expect(tkRBrace, "expected '}' to close module")
|
||||
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 =
|
||||
let loc = p.currentLoc
|
||||
@@ -1027,6 +1037,9 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
||||
var path: seq[string] = @[]
|
||||
path.add(p.expect(tkIdent, "expected import path segment").text)
|
||||
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()
|
||||
path.add(p.expect(tkIdent, "expected import path segment").text)
|
||||
var kind = ukSingle
|
||||
@@ -1040,13 +1053,17 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
||||
kind = ukGlob
|
||||
elif p.check(tkColonColon):
|
||||
discard p.advance()
|
||||
discard p.expect(tkLBrace, "expected '{' for multi-import")
|
||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||
names.add(p.expect(tkIdent, "expected name in multi-import").text)
|
||||
if p.check(tkComma):
|
||||
discard p.advance()
|
||||
discard p.expect(tkRBrace, "expected '}' to close multi-import")
|
||||
kind = ukMulti
|
||||
if p.check(tkLBrace):
|
||||
discard p.advance()
|
||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||
names.add(p.expect(tkIdent, "expected name in multi-import").text)
|
||||
if p.check(tkComma):
|
||||
discard p.advance()
|
||||
discard p.expect(tkRBrace, "expected '}' to close multi-import")
|
||||
kind = ukMulti
|
||||
elif p.check(tkStar):
|
||||
discard p.advance()
|
||||
kind = ukGlob
|
||||
if p.check(tkSemicolon):
|
||||
discard p.advance()
|
||||
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'")
|
||||
if p.check(tkFunc):
|
||||
let funcDecl = p.parseFuncDecl(isPublic, false, attrs)
|
||||
funcDecl.isPublic = isPublic
|
||||
return funcDecl
|
||||
# Convert dkFunc to dkExternFunc
|
||||
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):
|
||||
discard p.advance()
|
||||
var items: seq[Decl] = @[]
|
||||
|
||||
+37
-10
@@ -118,6 +118,16 @@ proc collectGlobals*(sema: var Sema) =
|
||||
sym.typ = makeFunc(params, retType)
|
||||
if not sema.globalScope.define(sym):
|
||||
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:
|
||||
let t = makeNamed(decl.declStructName)
|
||||
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.typeTable[decl.declAliasName] = t
|
||||
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:
|
||||
let name = decl.declUsePath[^1]
|
||||
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
|
||||
discard sema.globalScope.define(sym)
|
||||
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]
|
||||
if sema.globalScope.lookup(name) == nil:
|
||||
let sym = Symbol(kind: skModule, name: name, typ: makeUnknown(), isPublic: true)
|
||||
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:
|
||||
# Register interface for conformance checking
|
||||
sema.interfaceTable[decl.declInterfaceName] = decl
|
||||
@@ -460,10 +483,14 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
||||
return makeUnknown()
|
||||
of ekField:
|
||||
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
|
||||
if obj.name.endsWith("_Data"):
|
||||
let enumName = obj.name[0..^6] # Remove "_Data" suffix
|
||||
if objType.name.endsWith("_Data"):
|
||||
let enumName = objType.name[0..^6] # Remove "_Data" suffix
|
||||
let enumSym = sema.globalScope.lookup(enumName)
|
||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||
# 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:
|
||||
if nf.name == expr.exprFieldName:
|
||||
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:
|
||||
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
||||
else:
|
||||
let sym = sema.globalScope.lookup(obj.name)
|
||||
let sym = sema.globalScope.lookup(objType.name)
|
||||
if sym != nil and sym.decl != nil:
|
||||
if sym.decl.kind == dkStruct:
|
||||
for f in sym.decl.declStructFields:
|
||||
if f.name == expr.exprFieldName:
|
||||
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:
|
||||
# Algebraic enum fields
|
||||
if expr.exprFieldName == "tag":
|
||||
|
||||
@@ -120,6 +120,9 @@ proc isAssignableTo*(a, b: Type): bool =
|
||||
# smaller int -> int/uint
|
||||
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
|
||||
# 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
|
||||
if a.isNumeric and b.isNumeric: return false
|
||||
# bool across widths
|
||||
|
||||
Reference in New Issue
Block a user