use std::{collections::HashMap, sync::Arc};
use arcstr::Substr;
use tracing::trace;
use crate::{
symbols::name::SymName,
syntax::{
buildctx::BuildCtx, cst::{
tree::{Cst, NodeHandle},
view::{self, ExportItemKind, StmtKind, TypedefKind, View},
}, lexer::{bsv::BsvLexer, token::Token}, location::ROOT_ORIGIN, parser::{self, parser::Parser}, preprocessor::{
vpp::{VppPreprocessor, VppSpan}, Preprocessor
}, streams::TextCharStream
},
util::data::either::Either,
};
use super::{PkgHandle, Project, file::FileContents, querry::Queryable};
pub struct Syntax {
pub ctx: BuildCtx,
pub vpp: SyntaxVpp,
pub tokens: Vec<Token>, pub cst: Cst,
pub top: PkgTop,
}
pub struct SyntaxVpp {
pub spans: Vec<VppSpan>,
}
pub struct PkgTop {
pub defs: HashMap<SymName, NodeHandle>,
pub imports: Vec<PkgHandle>,
pub exports: Vec<Either<Substr, PkgHandle>>,
}
impl Queryable for Syntax {
type Input = PkgHandle;
type Ctx = ();
type Backref = ();
fn compute(input: Self::Input, _ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
let pkg = proj.get_pkg(input);
let file = proj.file_contents(pkg.file, input);
trace!("compiling syntax for {}", pkg);
Arc::new(Syntax::parse(proj, input, file))
}
fn invalidate(&self, input: &Self::Input, _ctx: &Self::Ctx, proj: &Project) {
proj.exports.invalidate(proj, input);
proj.bindings.invalidate(proj, input);
}
fn invalidate_backref(&self, _backref: Self::Backref, _proj: &Project) {}
}
impl Syntax {
pub fn parse(proj: &Project, pkg: PkgHandle, file: Arc<FileContents>) -> Self {
let mut ctx = BuildCtx::new(pkg, file.clone());
let source = file.chunks(ROOT_ORIGIN);
let source = TextCharStream::new(source.iter());
let mut preprocessed = Vec::new();
let mut vpp = VppPreprocessor::new(proj, &mut ctx);
vpp.process(source, &mut preprocessed);
let preprocessed = TextCharStream::new(preprocessed.iter());
let vpp = SyntaxVpp { spans: vpp.spans };
let mut tokens = Vec::new();
let mut lexer = BsvLexer::new(&mut ctx);
lexer.run(preprocessed, &mut tokens);
let tokens = tokens.clone();
let mut parser = Parser::new(&mut ctx, tokens.clone());
parser::bsv::package(&mut parser);
let nodes = parser.make_tree();
let cst = Cst::new(nodes);
let top = Self::compute_top(proj, pkg, &cst);
Syntax {
ctx,
vpp,
tokens,
cst,
top,
}
}
fn compute_top(proj: &Project, pkg: PkgHandle, cst: &Cst) -> PkgTop {
let mut top = PkgTop {
defs: HashMap::new(),
imports: Vec::new(),
exports: Vec::new(),
};
let root = view::Package::cast(cst.root());
let Some(body) = root.body() else {
return top;
};
for stmt in body.stmts() {
let Some(stmt) = stmt.kind() else {
continue;
};
match stmt {
StmtKind::Function(stmt_function) => {
let Some(name) = stmt_function.typed_name().and_then(|n| n.name()) else {
continue;
};
top.defs
.insert(SymName::Value(name.str()), stmt_function.syntax().handle());
}
StmtKind::Interface(intf) => {
intf.name().map(|n| {
top.defs
.insert(SymName::Type(n.str()), intf.syntax().handle())
});
}
StmtKind::Module(modl) => {
modl.name().map(|n| {
top.defs
.insert(SymName::Value(n.str()), modl.syntax().handle())
});
}
StmtKind::Import(stmt_import) => {
for import in stmt_import.items() {
let Some(name) = import.name() else { continue };
let Some(imported) = proj.get_pkg(pkg).resolve_import(proj, &name.str())
else {
continue;
};
top.imports.push(imported);
}
}
StmtKind::Export(stmt_export) => {
for export in stmt_export.items() {
let Some(kind) = export.kind() else { continue };
match kind {
ExportItemKind::Def(name) | ExportItemKind::Struct(name) => {
top.exports.push(Either::Left(name.str()));
}
ExportItemKind::Package(name) => {
let Some(exported) =
proj.get_pkg(pkg).resolve_import(proj, &name.str())
else {
continue;
};
top.exports.push(Either::Right(exported));
}
}
}
}
StmtKind::Typedef(tydef) => {
tydef.kind().map(|kind| match kind {
TypedefKind::Enum(tydef) => tydef.name().map(|n| {
top.defs
.insert(SymName::Type(n.str()), tydef.syntax().handle())
}),
TypedefKind::Struct(tydef) => tydef.name().map(|n| {
top.defs
.insert(SymName::Type(n.str()), tydef.syntax().handle())
}),
TypedefKind::Union(tydef) => tydef.name().map(|n| {
top.defs
.insert(SymName::Type(n.str()), tydef.syntax().handle())
}),
TypedefKind::Alias(tydef) => tydef.name().map(|n| {
top.defs
.insert(SymName::Type(n.str()), tydef.syntax().handle())
}),
});
}
StmtKind::Typeclass(tyclass) => {
if let Some(name) = tyclass.name() {
top.defs
.insert(SymName::Type(name.str()), tyclass.syntax().handle());
};
}
StmtKind::ExprOrBind(_) => (),
StmtKind::Instance(_) => (),
StmtKind::Method(_) => (),
}
}
top
}
pub fn root(&self) -> view::Package {
view::Package::try_cast(self.cst.root()).expect("CST root node is not 'Package'")
}
pub fn compute_globals(&self, proj: &Project) -> HashMap<SymName, (PkgHandle, NodeHandle)> {
let pkg = self.ctx.pkg;
let mut defs: HashMap<SymName, (PkgHandle, NodeHandle)> = HashMap::new();
for imported in &self.top.imports {
let exports = proj.exports.get_as(proj, *imported, pkg);
defs.extend(exports.defs.iter().map(|(k, v)| (k.clone(), *v)));
}
defs.extend(self.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
proj.get_pkg(pkg)
.name
.clone()
.map(|name| defs.insert(SymName::Pkg(name), (pkg, self.root().syntax().handle())));
defs
}
}