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:
2026-05-31 01:55:54 +03:00
parent 60d4260c93
commit c7114c0538
11 changed files with 316 additions and 67 deletions
+4 -2
View File
@@ -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)
+4
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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":
+3
View File
@@ -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
+44
View File
@@ -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;
}
}
+7
View File
@@ -0,0 +1,7 @@
[Package]
Name = "stdlib_test"
Version = "0.1.0"
Type = "bin"
[Build]
Output = "Bin"
+21
View File
@@ -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;
}