///|
/// `import a/b.{c, type D as E} as f`, with `import` consumed.
fn Parser::parse_import(
self : Parser,
import_start : Int32,
) -> @ast.Definition? raise ParseError {
let mut start = 0
let mut end = 0
let module_ = StringBuilder()
let mut last_segment_end = 0
// Gather module names
while true {
let (s, name, e) = self.expect_name(Module)
if module_.is_empty() {
start = s
} else {
module_.write_string("/")
}
module_.write_string(name)
end = e
last_segment_end = e
// Useful error for `import a/.{b}`
if self.maybe_one(SlashDot) is Some((slash, _)) {
parse_error(ExpectedName, sp(slash + 1, slash + 1))
}
if self.maybe_one(Slash) is None {
break
}
}
let module_name = module_.to_string()
let documentation = self.take_documentation(start).map(d => d.text)
let mut unqualified_values : Array[@ast.UnqualifiedImport] = []
let mut unqualified_types : Array[@ast.UnqualifiedImport] = []
if self.maybe_one(Dot) is Some((dot_start, dot_end)) {
try self.expect_one(LeftBrace) catch {
e => {
// `import gleam.io`: a Python-like import, unless the module has a
// `/` already.
if module_name.contains("/") {
raise e
}
match e {
ParseError(
error=UnexpectedToken(token=Name(item) | UpName(item), ..),
..
) =>
parse_error(
IncorrectImportModuleSeparator(module_=module_name, item~),
sp(dot_start, dot_end),
)
_ => raise e
}
}
} noraise {
_ => ()
}
let (values, types) = self.parse_unqualified_imports()
unqualified_values = values
unqualified_types = types
let (_, e) = self.expect_one(RightBrace)
end = e
}
let mut as_name : @ast.ImportAlias? = None
if self.maybe_one(Keyword(As)) is Some((as_start, _)) {
let (_, name, e) = self.expect_assign_name()
end = e
as_name = Some({ name, location: sp(as_start, end), })
}
Some(
Import({
documentation,
location: sp(import_start, end),
module_location: sp(start, last_segment_end),
module_: module_name,
as_name,
unqualified_values: unqualified_values[:],
unqualified_types: unqualified_types[:],
}),
)
}
///|
/// `[Name (as Name)? | UpName (as UpName)? | type UpName (as UpName)?](, ...)*,?`
/// Not a `series_of`: a trailing comma here is not recorded.
fn Parser::parse_unqualified_imports(
self : Parser,
) -> (Array[@ast.UnqualifiedImport], Array[@ast.UnqualifiedImport]) raise ParseError {
let values : Array[@ast.UnqualifiedImport] = []
let types : Array[@ast.UnqualifiedImport] = []
while true {
match self.token0 {
Some({ kind: Name(name), span, .. }) => {
self.advance()
let mut location = span
let mut as_alias : String? = None
if self.maybe_one(Keyword(As)) is Some(_) {
let (_, as_name, end) = self.expect_name(AsPattern)
as_alias = Some(as_name)
location = sp(span.start, end)
}
values.push({
name,
location,
name_position: span.start,
as_name: as_alias,
})
}
Some({ kind: UpName(name), span, .. }) => {
self.advance()
let mut location = span
let mut as_alias : String? = None
if self.maybe_one(Keyword(As)) is Some(_) {
let (_, as_name, end) = self.expect_upname()
as_alias = Some(as_name)
location = sp(span.start, end)
}
values.push({
name,
location,
name_position: span.start,
as_name: as_alias,
})
}
Some({ kind: Keyword(Type), span, .. }) => {
self.advance()
let (name_start, name, end) = self.expect_upname()
let mut location = sp(span.start, end)
let mut as_alias : String? = None
if self.maybe_one(Keyword(As)) is Some(_) {
let (_, as_name, e) = self.expect_upname()
as_alias = Some(as_name)
location = sp(span.start, e)
}
types.push({
name,
location,
name_position: name_start,
as_name: as_alias,
})
}
_ => break
}
if self.maybe_one(Comma) is None {
break
}
}
(values, types)
}
///|
/// `const name (: T)? = value`, with `const` (and `pub`) consumed.
fn Parser::parse_module_const(
self : Parser,
start : Int32,
public : Bool,
attributes : Attributes,
) -> @ast.Definition? raise ParseError {
let (name_start, name, name_end) = self.expect_name(Constant)
let documentation = self.take_documentation(name_start)
let annotation = self.parse_type_annotation(Colon)
let (eq_s, eq_e) = self.expect_one(Equal)
match self.parse_const_value() {
Some(value) => {
let annotation_end = match annotation {
Some(a) => a.location().end
None => 0
}
let publicity = publicity(public, attributes.internal)
Some(
ModuleConstant({
documentation,
// After the annotation when there is one, otherwise after the name.
location: sp(
start,
if annotation_end > name_end {
annotation_end
} else {
name_end
},
),
publicity,
name,
name_location: sp(name_start, name_end),
annotation,
value,
deprecation: attributes.deprecated,
}),
)
}
None => parse_error(NoValueAfterEqual, sp(eq_s, eq_e))
}
}