file-local / package-local decls
This commit is contained in:
+111
-56
@@ -31,7 +31,12 @@ Parser :: struct {
|
||||
capture_pipe: bool,
|
||||
// A parenthesized `if` condition ends before a leading-dot brace-less body.
|
||||
if_condition: bool,
|
||||
hidden_names: [dynamic]symbol.Id,
|
||||
local_names: [dynamic]Local_Name,
|
||||
}
|
||||
|
||||
Local_Name :: struct {
|
||||
name: symbol.Id,
|
||||
visibility: types.Visibility,
|
||||
}
|
||||
|
||||
MAX_EXPRESSION_NESTING :: 256
|
||||
@@ -43,16 +48,16 @@ token_text :: proc(parser: ^Parser, tok: token.Token) -> string {
|
||||
return parser.source_file.text[int(tok.span.start):int(tok.span.end)]
|
||||
}
|
||||
|
||||
package_hidden_name :: proc(parser: ^Parser, name: symbol.Id) -> bool {
|
||||
for hidden in parser.hidden_names {
|
||||
if hidden == name {
|
||||
return true
|
||||
declaration_visibility :: proc(parser: ^Parser, name: symbol.Id) -> types.Visibility {
|
||||
for local in parser.local_names {
|
||||
if local.name == name {
|
||||
return local.visibility
|
||||
}
|
||||
}
|
||||
return false
|
||||
return .Public
|
||||
}
|
||||
|
||||
collect_hidden_names :: proc(parser: ^Parser) {
|
||||
collect_local_names :: proc(parser: ^Parser) {
|
||||
depth := 0
|
||||
for item, index in parser.tokens.items {
|
||||
#partial switch item.kind {
|
||||
@@ -61,9 +66,32 @@ collect_hidden_names :: proc(parser: ^Parser) {
|
||||
case .Right_Brace:
|
||||
depth = max(depth-1, 0)
|
||||
case .Keyword_Hide:
|
||||
if depth == 0 && index+1 < len(parser.tokens.items) &&
|
||||
parser.tokens.items[index+1].kind == .Identifier {
|
||||
append(&parser.hidden_names, parser.tokens.items[index+1].symbol)
|
||||
if depth != 0 {
|
||||
continue
|
||||
}
|
||||
visibility := types.Visibility.Package
|
||||
name_index := index+1
|
||||
if name_index < len(parser.tokens.items) &&
|
||||
parser.tokens.items[name_index].kind == .Left_Paren {
|
||||
if index+4 >= len(parser.tokens.items) ||
|
||||
parser.tokens.items[index+2].kind != .Identifier ||
|
||||
parser.tokens.items[index+3].kind != .Right_Paren {
|
||||
continue
|
||||
}
|
||||
scope := token_text(parser, parser.tokens.items[index+2])
|
||||
if scope == "file" {
|
||||
visibility = .File
|
||||
} else if scope != "package" {
|
||||
continue
|
||||
}
|
||||
name_index = index+4
|
||||
}
|
||||
if name_index < len(parser.tokens.items) &&
|
||||
parser.tokens.items[name_index].kind == .Identifier {
|
||||
append(&parser.local_names, Local_Name{
|
||||
name=parser.tokens.items[name_index].symbol,
|
||||
visibility=visibility,
|
||||
})
|
||||
}
|
||||
case:
|
||||
}
|
||||
@@ -437,7 +465,7 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
u32(name.symbol),
|
||||
u32(qualifier),
|
||||
u32(parser.file),
|
||||
!symbol.is_valid(qualifier) && package_hidden_name(parser, name.symbol),
|
||||
types.Visibility.Public if symbol.is_valid(qualifier) else declaration_visibility(parser, name.symbol),
|
||||
)
|
||||
if token_text(parser, name) == "struct_type" &&
|
||||
current(parser).kind == .Bang && peek(parser).kind == .Left_Paren {
|
||||
@@ -2605,7 +2633,7 @@ parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
return id
|
||||
}
|
||||
|
||||
parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden: bool) {
|
||||
parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool, visibility: types.Visibility) {
|
||||
advance(parser)
|
||||
if _, ok := allow(parser, .Left_Paren); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '(' after 'func'")
|
||||
@@ -2633,7 +2661,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
c_abi=c_abi,
|
||||
package_hidden=package_hidden,
|
||||
visibility=visibility,
|
||||
has_body=false,
|
||||
variadic=variadic,
|
||||
params=params,
|
||||
@@ -2653,7 +2681,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
c_abi=c_abi,
|
||||
package_hidden=package_hidden,
|
||||
visibility=visibility,
|
||||
has_body=true,
|
||||
variadic=variadic,
|
||||
params=params,
|
||||
@@ -2838,9 +2866,9 @@ parse_inline_union_type :: proc(parser: ^Parser) -> types.Type {
|
||||
return types.union_anonymous(&parser.module.type_store, fields[:], tag)
|
||||
}
|
||||
|
||||
parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidden: bool, is_union := false) {
|
||||
parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, visibility: types.Visibility, is_union := false) {
|
||||
start := advance(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility)
|
||||
// A tagged union spells its discriminant in parens: `union(Enum)` reuses an existing
|
||||
// enum; `union(enum)` synthesizes one from the variant names after the body is parsed.
|
||||
tag := types.INVALID
|
||||
@@ -2874,7 +2902,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde
|
||||
"native union declarations require a body" if is_union else "native struct declarations require a body",
|
||||
)
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag, declared_tag=declared_tag, package_hidden=package_hidden) {
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag, declared_tag=declared_tag, visibility=visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if !ended_by_newline {
|
||||
@@ -2911,7 +2939,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde
|
||||
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
||||
tag = synthesize_union_tag(parser, fields[:])
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple, package_hidden=package_hidden) {
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple, visibility=visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
} else if !c_layout && !is_union && !tuple {
|
||||
for value, index in defaults {
|
||||
@@ -2930,10 +2958,10 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_opaque :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
parse_opaque :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) {
|
||||
start := advance(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden)
|
||||
if !types.define_record(&parser.module.type_store, id, nil, false, true, false, package_hidden=package_hidden) {
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility)
|
||||
if !types.define_record(&parser.module.type_store, id, nil, false, true, false, visibility=visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if current(parser).kind == .Left_Brace {
|
||||
@@ -2958,11 +2986,11 @@ synthesize_union_tag :: proc(parser: ^Parser, fields: []types.Field) -> types.Ty
|
||||
return types.enum_anonymous(&parser.module.type_store, members, types.U16)
|
||||
}
|
||||
|
||||
parse_distinct :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
parse_distinct :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) {
|
||||
start := advance(parser)
|
||||
child := parse_type(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden)
|
||||
if !types.define_distinct(&parser.module.type_store, id, child, package_hidden) {
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility)
|
||||
if !types.define_distinct(&parser.module.type_store, id, child, visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if !types.is_valid(child) {
|
||||
@@ -2971,7 +2999,7 @@ parse_distinct :: proc(parser: ^Parser, name: token.Token, package_hidden: bool)
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
parse_alias :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) {
|
||||
start := advance(parser)
|
||||
saved := parser.cursor
|
||||
if current(parser).kind == .Identifier && peek(parser).kind == .Dot {
|
||||
@@ -2988,7 +3016,7 @@ parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
target_pkg=ast.INVALID_PACKAGE,
|
||||
package_hidden=package_hidden,
|
||||
visibility=visibility,
|
||||
valid=true,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -2999,8 +3027,8 @@ parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
}
|
||||
parser.cursor = saved
|
||||
child := parse_type(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden)
|
||||
if !types.define_alias(&parser.module.type_store, id, child, package_hidden) {
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility)
|
||||
if !types.define_alias(&parser.module.type_store, id, child, visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if !types.is_valid(child) {
|
||||
@@ -3143,7 +3171,7 @@ parse_inline_enum_type :: proc(parser: ^Parser) -> types.Type {
|
||||
return types.enum_anonymous(&parser.module.type_store, members[:], backing)
|
||||
}
|
||||
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) {
|
||||
start := advance(parser)
|
||||
explicit_backing := false
|
||||
backing := types.INVALID
|
||||
@@ -3165,8 +3193,8 @@ parse_enum :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
}
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden)
|
||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing, package_hidden) {
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility)
|
||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing, visibility) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if explicit_backing && deferred {
|
||||
@@ -3295,10 +3323,37 @@ parse_test :: proc(parser: ^Parser, name: token.Token) {
|
||||
}
|
||||
|
||||
parse_top_level :: proc(parser: ^Parser) {
|
||||
hide_token, package_hidden := allow(parser, .Keyword_Hide)
|
||||
modifier := current(parser)
|
||||
visibility := types.Visibility.Public
|
||||
has_modifier := modifier.kind == .Keyword_Hide
|
||||
if has_modifier {
|
||||
visibility = .Package
|
||||
advance(parser)
|
||||
if _, scoped := allow(parser, .Left_Paren); scoped {
|
||||
if current(parser).kind == .Identifier {
|
||||
scope := advance(parser)
|
||||
scope_text := token_text(parser, scope)
|
||||
if scope_text == "file" {
|
||||
visibility = .File
|
||||
} else if scope_text != "package" {
|
||||
source.addf(
|
||||
parser.diagnostics,
|
||||
scope.span,
|
||||
"unknown hide scope '%s'; expected 'package' or 'file'",
|
||||
scope_text,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected 'package' or 'file' in hide scope")
|
||||
}
|
||||
if _, closed := allow(parser, .Right_Paren); !closed {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after hide scope")
|
||||
}
|
||||
}
|
||||
}
|
||||
if current(parser).kind == .Keyword_Test && peek(parser).kind == .Keyword_Import {
|
||||
if package_hidden {
|
||||
source.add(parser.diagnostics, hide_token.span, "test imports cannot use 'hide'")
|
||||
if has_modifier {
|
||||
source.add(parser.diagnostics, modifier.span, "test imports cannot use a visibility modifier")
|
||||
}
|
||||
start := advance(parser)
|
||||
advance(parser) // consume 'import'
|
||||
@@ -3306,15 +3361,15 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
return
|
||||
}
|
||||
if current(parser).kind == .Keyword_Import {
|
||||
if package_hidden {
|
||||
source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'")
|
||||
if has_modifier {
|
||||
source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier")
|
||||
}
|
||||
start := advance(parser)
|
||||
parse_import(parser, token.Token{}, start)
|
||||
return
|
||||
}
|
||||
if current(parser).kind != .Identifier {
|
||||
message := "expected a declaration name after 'hide'" if package_hidden else "expected a top-level declaration"
|
||||
message := "expected a declaration name after visibility modifier" if has_modifier else "expected a top-level declaration"
|
||||
source.add(parser.diagnostics, current(parser).span, message)
|
||||
for current(parser).kind != .Newline && current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
@@ -3324,8 +3379,8 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
}
|
||||
name := advance(parser)
|
||||
if current(parser).kind == .Keyword_Test {
|
||||
if package_hidden {
|
||||
source.add(parser.diagnostics, hide_token.span, "test declarations cannot use 'hide'")
|
||||
if has_modifier {
|
||||
source.add(parser.diagnostics, modifier.span, "test declarations cannot use a visibility modifier")
|
||||
}
|
||||
parse_test(parser, name)
|
||||
return
|
||||
@@ -3335,8 +3390,8 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
advance(parser)
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Keyword_Import {
|
||||
if package_hidden {
|
||||
source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'")
|
||||
if has_modifier {
|
||||
source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier")
|
||||
}
|
||||
start := advance(parser)
|
||||
parse_import(parser, name, start)
|
||||
@@ -3346,7 +3401,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
}
|
||||
if current(parser).kind == .Keyword_Func || current(parser).kind == .Keyword_C_Func {
|
||||
c_abi := current(parser).kind == .Keyword_C_Func
|
||||
parse_function(parser, name, c_abi, package_hidden)
|
||||
parse_function(parser, name, c_abi, visibility)
|
||||
return
|
||||
}
|
||||
type_syntax := types.INVALID
|
||||
@@ -3375,32 +3430,32 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
source.add(parser.diagnostics, span_from(name.span, current(parser).span),
|
||||
"function declarations do not use '::'; write 'name func(...)' or 'name c_func(...)'")
|
||||
c_abi := current(parser).kind == .Keyword_C_Func
|
||||
parse_function(parser, name, c_abi, package_hidden)
|
||||
parse_function(parser, name, c_abi, visibility)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon &&
|
||||
(current(parser).kind == .Keyword_Struct || current(parser).kind == .Keyword_C_Struct) {
|
||||
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct, package_hidden)
|
||||
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct, visibility)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque {
|
||||
parse_opaque(parser, name, package_hidden)
|
||||
parse_opaque(parser, name, visibility)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union {
|
||||
parse_struct(parser, name, false, package_hidden, is_union=true)
|
||||
parse_struct(parser, name, false, visibility, is_union=true)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum {
|
||||
parse_enum(parser, name, package_hidden)
|
||||
parse_enum(parser, name, visibility)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct {
|
||||
parse_distinct(parser, name, package_hidden)
|
||||
parse_distinct(parser, name, visibility)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Alias {
|
||||
parse_alias(parser, name, package_hidden)
|
||||
parse_alias(parser, name, visibility)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3411,7 +3466,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
name=name.symbol,
|
||||
pkg=parser.pkg,
|
||||
file=parser.file,
|
||||
package_hidden=package_hidden,
|
||||
visibility=visibility,
|
||||
type=type_syntax,
|
||||
immutable=operator.kind == .Colon_Colon,
|
||||
expr=expr,
|
||||
@@ -3432,9 +3487,9 @@ parse :: proc(
|
||||
diagnostics=diagnostics,
|
||||
module=ast.init_module(allocator),
|
||||
}
|
||||
parser.hidden_names.allocator = allocator
|
||||
defer delete(parser.hidden_names)
|
||||
collect_hidden_names(&parser)
|
||||
parser.local_names.allocator = allocator
|
||||
defer delete(parser.local_names)
|
||||
collect_local_names(&parser)
|
||||
skip_newlines(&parser)
|
||||
for current(&parser).kind != .Eof {
|
||||
parse_top_level(&parser)
|
||||
@@ -3459,9 +3514,9 @@ parse_into :: proc(
|
||||
pkg=pkg,
|
||||
file=file,
|
||||
}
|
||||
parser.hidden_names.allocator = module.allocator
|
||||
defer delete(parser.hidden_names)
|
||||
collect_hidden_names(&parser)
|
||||
parser.local_names.allocator = module.allocator
|
||||
defer delete(parser.local_names)
|
||||
collect_local_names(&parser)
|
||||
skip_newlines(&parser)
|
||||
for current(&parser).kind != .Eof {
|
||||
parse_top_level(&parser)
|
||||
|
||||
Reference in New Issue
Block a user