// Module fields, and the entry point.
///|
/// A module read from was source.
pub struct Read {
fields : @ast.LocModule
sources : @er.Sources
source : @er.SourceId
diagnostics : Array[@er.Report]
}
///|
/// The Wax module fields.
pub fn Read::fields(self : Self) -> @ast.LocModule {
self.fields
}
///|
/// The source registry the reports point into.
pub fn Read::sources(self : Self) -> @er.Sources {
self.sources
}
///|
/// The id of the file that was read.
pub fn Read::source(self : Self) -> @er.SourceId {
self.source
}
///|
/// Problems found while reading.
pub fn Read::diagnostics(self : Self) -> Array[@er.Report] {
self.diagnostics
}
///|
/// Read was source into the Wax AST.
///
/// Grouping errors come from shrubbery and are re-raised unchanged: there is
/// no value in was paraphrasing them, and a shrubbery diagnostic already
/// renders through error-report.
pub fn read_module(
text : String,
fname? : String = "input.was",
) -> Read raise ReadError {
let sources = @er.Sources::new()
let source = sources.add(fname, text)
let parsed = @shparser.parse(text) catch {
@sherror.ShrubberyError(d) => raise ReadError(d.to_report(source))
}
let groups = children(parsed.root())
let r = Reader::new(fname~, text~, src=source)
collect_type_names(r, groups)
let fields : @ast.LocModule = []
for g in groups {
match r.module_field(g) {
Some(f) => fields.push(f)
None => ()
}
}
{ fields, sources, source, diagnostics: r.diagnostics, }
}
///|
/// Every type name the file declares, for the one place the reader needs them:
/// telling `ints[0]` the literal from `xs[0]` the index.
fn collect_type_names(r : Reader, groups : Array[@sh.Node]) -> Unit {
fn scan(gs : Array[@sh.Node]) -> Unit {
for g in gs {
let p = split(g)
let head = p.head
if head.length() >= 2 &&
is_id(head[0], "type") &&
as_id(head[1]) is Some(n) {
r.type_names[n] = ()
}
if head.length() >= 1 && is_id(head[0], "rec") {
match p.block {
Some(inner) => scan(inner)
None => ()
}
}
}
}
scan(groups)
}
///|
/// Modifiers that may precede a declaration.
priv struct Mods {
attrs : Array[@ast.Attribute]
}
///|
fn Reader::mods(
self : Reader,
head : Array[@sh.Node],
) -> (Mods, Array[@sh.Node]) raise ReadError {
let m : Mods = { attrs: [], }
let mut i = 0
while i < head.length() {
let at = self.loc(node_span(head[i]))
match as_id(head[i]) {
Some("export") => {
if i + 1 >= head.length() || as_str(head[i + 1]) is None {
fail_at(
"`export` takes the exported name as a string",
node_span(head[i]),
source=self.src,
)
}
m.attrs.push({
attr_name: "export",
attr_value: Some(
self.instr(
Str(None, @utf8.encode(as_str(head[i + 1]).unwrap())),
node_span(head[i + 1]),
),
),
attr_guard: None,
attr_span: at,
})
i += 2
}
Some("start") => {
m.attrs.push({
attr_name: "start",
attr_value: None,
attr_guard: None,
attr_span: at,
})
i += 1
}
Some("import") =>
if i + 2 < head.length() && as_str(head[i + 1]) is Some(n) {
m.attrs.push({
attr_name: "import",
attr_value: Some(
self.instr(Str(None, @utf8.encode(n)), node_span(head[i + 1])),
),
attr_guard: None,
attr_span: at,
})
i += 2
} else {
break
}
_ => break
}
}
(m, head[i:].to_owned())
}
///|
/// One top-level group as a module field.
fn Reader::module_field(
self : Reader,
g : @sh.Node,
) -> @basic.Annotated[@ast.ModuleField[@basic.Location], @basic.Location]? raise ReadError {
let p = split(g)
let (mods, head) = self.mods(p.head)
if head.length() == 0 {
self.error("expected a declaration", p.span)
return None
}
let at = self.loc(p.span)
let field : @ast.ModuleField[@basic.Location]? = match as_id(head[0]) {
Some("feature") => {
if head.length() != 2 || as_str(head[1]) is None {
fail_at("`feature` takes one string", p.span, source=self.src)
}
Some(
ModuleAnnotation([
{
attr_name: "feature",
attr_value: Some(
self.instr(
Str(None, @utf8.encode(as_str(head[1]).unwrap())),
node_span(head[1]),
),
),
attr_guard: None,
attr_span: at,
},
]),
)
}
Some("type") => Some(Type([self.type_entry(head, p)]))
Some("rec") => {
let entries = []
match p.block {
Some(gs) =>
for tg in gs {
let tp = split(tg)
if !(tp.head.length() > 0 && is_id(tp.head[0], "type")) {
fail_at(
"a `rec` block contains only `type` declarations",
tp.span,
source=self.src,
)
}
entries.push(self.type_entry(tp.head, tp))
}
None =>
fail_at(
"expected `:` and a body after `rec`",
p.span,
source=self.src,
)
}
Some(Type(entries))
}
Some("fn") => Some(self.fn_field(head, p, mods))
Some("const") => Some(self.global_field(head, p, mods, false))
Some("let") => Some(self.global_field(head, p, mods, true))
Some("tag") => Some(self.tag_field(head, p, mods))
Some("memory") => Some(self.memory_field(head, p, mods))
Some("table") => Some(self.table_field(head, p, mods))
Some("elem") => Some(self.elem_field(head, p, mods))
Some("data") => Some(self.data_field(head, p, mods))
Some("import") => Some(self.import_field(head, p))
Some("cfg") => Some(self.cfg_field(head, p))
_ => {
self.error(
"expected a declaration",
p.span,
help="`type`, `rec`, `fn`, `const`, `let`, `tag`, `memory`, `table`, `elem`, `data`, `import`, `feature` or `cfg`",
)
None
}
}
match field {
Some(f) => Some({ desc: f, info: at, })
None => None
}
}