-
Notifications
You must be signed in to change notification settings - Fork 8
/
attributes.oc
128 lines (116 loc) · 4.15 KB
/
attributes.oc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Types and functions for working with attributes
import std::span::{ Span }
import std::mem
import std::vector::{ Vector }
import std::compact_map::{ Map }
import @tokens::{ Token }
import @parser::{ Parser }
import @errors::{ Error }
enum AttributeType {
Extern // [extern] or [extern "function_name"] to mark a symbol as external
Exits // [exits] to mark a function as non-returning
VariadicFormat // [variadic_format] to expand format-strings to varargs
Export // [export] to re-export an imported object
Formatting // [formatting <specifiers> <args>] to specify formatting for an object
Operator // [operator <op>] to specify an operator overload
Atomic // [atomic] to mark a type as Atomic
Alive // [alive] to explicitly mark a function as not dead
Invalid // used for error reporting
}
def AttributeType::hash(this): u32 => this as u32
def AttributeType::eq(this, other: AttributeType): bool => this == other
def AttributeType::from_str(s: str): AttributeType => match s {
"extern" => Extern
"exits" => Exits
"variadic_format" => VariadicFormat
"export" => Export
"formatting" => Formatting
"operator" => Operator
"atomic" => Atomic
"alive" => Alive
else => Invalid
}
struct Attribute {
type: AttributeType
args: &Vector<str>
span: Span
}
def Attribute::new(type: AttributeType, span: Span): &Attribute {
let attr = mem::alloc<Attribute>()
attr.type = type
attr.args = Vector<str>::new(capacity: 2)
attr.span = span
return attr
}
def Attribute::free(&this) {
.args.free()
mem::free(this)
}
def Attribute::validate(&this, parser_for_errors: &Parser): bool {
match this.type {
Extern => {
if .args.size > 1 {
parser_for_errors.error(Error::new(
this.span,
"Extern attribute takes at most one argument"
))
return false
}
}
Formatting => {
if .args.size < 1 or .args.size > 2 {
parser_for_errors.error(Error::new_note(
this.span,
"Incorrect number of arguments for formatting attribute",
"Only one or two arguments are allowed"
))
return false
}
// If we have a second argument, it should contain `$` representing the
// object itself, which will be replaced with the object's name at codegen
if .args.size == 2 {
let found_dollar = false
for c : .args.at(1).chars() {
if c == '$' then found_dollar = true
}
if not found_dollar {
parser_for_errors.error(Error::new(
this.span,
"Second argument of formatting attribute must contain '$'"
))
return false
}
// Otherwise, just default to the object's name
} else {
.args.push("$")
}
}
Exits | VariadicFormat | Export | Atomic | Alive => {
if .args.size > 0 {
parser_for_errors.error(Error::new(
this.span,
f"{.type} attribute takes no arguments"
))
return false
}
}
Invalid => {
parser_for_errors.error(Error::new(
this.span,
"Invalid attribute"
))
return false
}
Operator => {
if .args.size != 1 {
parser_for_errors.error(Error::new(
this.span,
"Operator attribute takes exactly one argument"
))
return false
}
// We will check the operator name in parsing phase
}
}
return true
}