Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 21 additions & 82 deletions src/semantic/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,135 +15,74 @@ import (
// InjectBuiltins adds core builtin symbols to the provided scope so that
// parsing/binding/checking can reference them.
// 'print' accepts any single argument (empty type means unconstrained) and returns void.
func builtinFunc(name, returnType string, params []ast.Parameter) *ast.FuncDecl {
return &ast.FuncDecl{
Name: name,
NameTok: tokens.Token{},
Params: params,
ReturnType: returnType,
}
}

func InjectBuiltins(scope *Scope) {
_ = scope.Define(Symbol{
Name: "print",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "print",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "value", Type: ""}},
ReturnType: "void",
},
Node: builtinFunc("print", "void", []ast.Parameter{{Name: "value", Type: ""}}),
})

_ = scope.Define(Symbol{
Name: "println",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "println",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "value", Type: ""}},
ReturnType: "void",
},
Node: builtinFunc("println", "void", []ast.Parameter{{Name: "value", Type: ""}}),
})

_ = scope.Define(Symbol{
Name: "printf",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "printf",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "value", Type: ""}, {Name: "args", Type: "[]any"}},
ReturnType: "void",
},
Node: builtinFunc("printf", "void", []ast.Parameter{{Name: "value", Type: ""}, {Name: "args", Type: "[]any"}}),
})

_ = scope.Define(Symbol{
Name: "exit",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "exit",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "code", Type: "i32"}},
},
Node: builtinFunc("exit", "", []ast.Parameter{{Name: "code", Type: "i32"}}),
})

_ = scope.Define(Symbol{
Name: "input",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "input",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "prompt", Type: "string"}},
ReturnType: "string",
},
Node: builtinFunc("input", "string", []ast.Parameter{{Name: "prompt", Type: "string"}}),
})

// range built-in
_ = scope.Define(Symbol{
Name: "range",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "range",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "a", Type: "i32"}, {Name: "b", Type: "i32"}, {Name: "c", Type: "i32"}},
ReturnType: "[]i32",
},
Node: builtinFunc("range", "[]i32", []ast.Parameter{{Name: "a", Type: "i32"}, {Name: "b", Type: "i32"}, {Name: "c", Type: "i32"}}),
})

// array constructor: array(type, size[, init]) -> []type (runtime uses []any)
_ = scope.Define(Symbol{
Name: "array",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "array",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "type", Type: ""}, {Name: "size", Type: "i32"}, {Name: "init", Type: ""}},
ReturnType: "[]any",
},
Node: builtinFunc("array", "[]any", []ast.Parameter{{Name: "type", Type: ""}, {Name: "size", Type: "i32"}, {Name: "init", Type: ""}}),
})

// ARC helpers: weak, upgrade, refcount
_ = scope.Define(Symbol{
Name: "weak",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "weak",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "x", Type: ""}},
ReturnType: "any",
},
Node: builtinFunc("weak", "any", []ast.Parameter{{Name: "x", Type: ""}}),
})

_ = scope.Define(Symbol{
Name: "upgrade",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "upgrade",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "w", Type: ""}},
ReturnType: "any",
},
Node: builtinFunc("upgrade", "any", []ast.Parameter{{Name: "w", Type: ""}}),
})

_ = scope.Define(Symbol{
Name: "refcount",

Kind: SymFunc,
Node: &ast.FuncDecl{
Name: "refcount",

NameTok: tokens.Token{},
Params: []ast.Parameter{{Name: "x", Type: ""}},
ReturnType: "i32",
},
Node: builtinFunc("refcount", "i32", []ast.Parameter{{Name: "x", Type: ""}}),
})
}

Expand Down
23 changes: 15 additions & 8 deletions src/semantic/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,36 @@ func formatAssign(target, value string) string {
return fmt.Sprintf("cannot assign %s to %s", value, target)
}

func parseTypeName(name string) Type {
var typeInfoCache = map[string]Type{}

func typeInfoFor(name string) Type {
if name == "" {
return Type{}
}
return ParseType(name)
if info, ok := typeInfoCache[name]; ok {
return info
}
info := ParseType(name)
typeInfoCache[name] = info
return info
}

func isScalar(info Type) bool {
return info.ArrayDepth == 0 && !info.Nullable
}

func isIntegerName(name string) bool {
info := parseTypeName(name)
info := typeInfoFor(name)
return isScalar(info) && IsIntegerToken(info.Base)
}

func isFloatName(name string) bool {
info := parseTypeName(name)
info := typeInfoFor(name)
return isScalar(info) && IsFloatToken(info.Base)
}

func isNumericName(name string) bool {
info := parseTypeName(name)
info := typeInfoFor(name)
return isScalar(info) && IsNumericToken(info.Base)
}

Expand Down Expand Up @@ -120,7 +127,7 @@ func checkGlobal(g *ast.GlobalVarDecl, ts *typeScope, table *TypeTable, module *
*diags = append(*diags, CheckDiag{Message: formatAssign(g.Type, vt), Span: g.Tok.Span})
}
if g.Type != "" {
gt := parseTypeName(g.Type)
gt := typeInfoFor(g.Type)
if isScalar(gt) && (IsIntegerToken(gt.Base) || IsFloatToken(gt.Base) || gt.Base == tokens.TokenBit) {
if ok, msg := literalFitsType(g.Type, g.Value); !ok {
*diags = append(*diags, CheckDiag{Message: msg, Span: g.Value.Span()})
Expand Down Expand Up @@ -729,7 +736,7 @@ func inferMetadataAccessType(x *ast.MemberAccessExpr, ts *typeScope, table *Type
// First try to infer the left side as an expression (for instance metadata)
objType := inferExprType(x.Object, ts, table, module, diags)
if objType != "" {
resType, ok := metadataReturnFor(objType, ParseType(objType), memberToken)
resType, ok := metadataReturnFor(objType, typeInfoFor(objType), memberToken)
if ok {
table.NodeToType[x] = resType.TypeName
table.NodeToTypeInfo[x] = resType.TypeInfo
Expand All @@ -739,7 +746,7 @@ func inferMetadataAccessType(x *ast.MemberAccessExpr, ts *typeScope, table *Type

// Fallback to type metadata (for builtin and custom types)
if id, ok := x.Object.(*ast.IdentifierExpr); ok {
resType, ok := metadataReturnFor(id.Name, ParseType(id.Name), memberToken)
resType, ok := metadataReturnFor(id.Name, typeInfoFor(id.Name), memberToken)
if ok {
table.NodeToType[x] = resType.TypeName
table.NodeToTypeInfo[x] = resType.TypeInfo
Expand Down
Loading