1use std::{collections::HashMap, sync::Arc};
2
3use arcstr::Substr;
4use tracing::trace;
5
6use crate::{
7 symbols::name::SymName,
8 syntax::{
9 buildctx::BuildCtx,
10 cst::{
11 tree::{Cst, NodeHandle},
12 view::{self, ExportItemKind, StmtKind, TypedefKind, View},
13 },
14 lexer::{bsv::BsvLexer, token::Token},
15 location::ROOT_ORIGIN,
16 parser::{self, state::Parser},
17 preprocessor::{
18 Preprocessor,
19 vpp::{VppPreprocessor, VppSpan},
20 },
21 streams::TextCharStream,
22 },
23 util::data::either::Either,
24};
25
26use super::{PkgHandle, Project, file::FileContents, querry::Queryable};
27
28pub struct Syntax {
30 pub ctx: BuildCtx,
31 pub vpp: SyntaxVpp,
32 pub tokens: Vec<Token>, pub cst: Cst,
34 pub top: PkgTop,
35}
36
37pub struct SyntaxVpp {
38 pub spans: Vec<VppSpan>,
39}
40
41pub struct PkgTop {
42 pub defs: HashMap<SymName, NodeHandle>,
43 pub imports: Vec<PkgHandle>,
44 pub exports: Vec<Either<Substr, PkgHandle>>,
45}
46
47impl Queryable for Syntax {
48 type Input = PkgHandle;
49 type Ctx = ();
50 type Backref = ();
51
52 fn compute(input: Self::Input, _ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
53 let pkg = proj.get_pkg(input);
54 let file = proj.file_contents(pkg.file, input);
55 trace!("compiling syntax for {}", pkg);
56 Arc::new(Syntax::parse(proj, input, file))
57 }
58
59 fn invalidate(&self, input: &Self::Input, _ctx: &Self::Ctx, proj: &Project) {
60 proj.exports.invalidate(proj, input);
61 proj.bindings.invalidate(proj, input);
62 }
63
64 fn invalidate_backref(&self, _backref: Self::Backref, _proj: &Project) {}
65}
66
67impl Syntax {
68 pub fn parse(proj: &Project, pkg: PkgHandle, file: Arc<FileContents>) -> Self {
69 let mut ctx = BuildCtx::new(pkg, file.clone());
70
71 let source = file.chunks(ROOT_ORIGIN);
72 let source = TextCharStream::new(source.iter());
73
74 let mut preprocessed = Vec::new();
75 let mut vpp = VppPreprocessor::new(proj, &mut ctx);
76 vpp.process(source, &mut preprocessed);
77 let preprocessed = TextCharStream::new(preprocessed.iter());
78
79 let vpp = SyntaxVpp { spans: vpp.spans };
80
81 let mut tokens = Vec::new();
82 let mut lexer = BsvLexer::new(&mut ctx);
83 lexer.run(preprocessed, &mut tokens);
84
85 let tokens = tokens.clone(); let mut parser = Parser::new(&mut ctx, tokens.clone());
88 parser::bsv::package(&mut parser);
89 let nodes = parser.make_tree();
90 let cst = Cst::new(nodes);
91
92 let top = Self::compute_top(proj, pkg, &cst);
93
94 Syntax {
95 ctx,
96 vpp,
97 tokens,
98 cst,
99 top,
100 }
101 }
102
103 fn compute_top(proj: &Project, pkg: PkgHandle, cst: &Cst) -> PkgTop {
104 let mut top = PkgTop {
105 defs: HashMap::new(),
106 imports: Vec::new(),
107 exports: Vec::new(),
108 };
109
110 let root = view::Package::cast(cst.root());
111
112 let Some(body) = root.body() else {
113 return top;
114 };
115
116 for stmt in body.stmts() {
117 let Some(stmt) = stmt.kind() else {
118 continue;
119 };
120 match stmt {
121 StmtKind::Function(stmt_function) => {
122 let Some(name) = stmt_function.typed_name().and_then(|n| n.name()) else {
123 continue;
124 };
125 top.defs
126 .insert(SymName::Value(name.str()), stmt_function.syntax().handle());
127 }
128 StmtKind::Interface(intf) => {
129 intf.name().map(|n| {
130 top.defs
131 .insert(SymName::Type(n.str()), intf.syntax().handle())
132 });
133 }
134 StmtKind::Module(modl) => {
135 modl.name().map(|n| {
136 top.defs
137 .insert(SymName::Value(n.str()), modl.syntax().handle())
138 });
139 }
140 StmtKind::Import(stmt_import) => {
141 for import in stmt_import.items() {
142 let Some(name) = import.name() else { continue };
143 let Some(imported) = proj.get_pkg(pkg).resolve_import(proj, &name.str())
144 else {
145 continue;
146 };
147 top.imports.push(imported);
148 }
149 }
150 StmtKind::Export(stmt_export) => {
151 for export in stmt_export.items() {
152 let Some(kind) = export.kind() else { continue };
153 match kind {
154 ExportItemKind::Def(name) | ExportItemKind::Struct(name) => {
155 top.exports.push(Either::Left(name.str()));
156 }
157 ExportItemKind::Package(name) => {
158 let Some(exported) =
160 proj.get_pkg(pkg).resolve_import(proj, &name.str())
161 else {
162 continue;
163 };
164 top.exports.push(Either::Right(exported));
165 }
166 }
167 }
168 }
169 StmtKind::Typedef(tydef) => {
170 tydef.kind().map(|kind| match kind {
171 TypedefKind::Enum(tydef) => tydef.name().map(|n| {
172 top.defs
173 .insert(SymName::Type(n.str()), tydef.syntax().handle())
174 }),
175 TypedefKind::Struct(tydef) => tydef.name().map(|n| {
176 top.defs
177 .insert(SymName::Type(n.str()), tydef.syntax().handle())
178 }),
179 TypedefKind::Union(tydef) => tydef.name().map(|n| {
180 top.defs
181 .insert(SymName::Type(n.str()), tydef.syntax().handle())
182 }),
183 TypedefKind::Alias(tydef) => tydef.name().map(|n| {
184 top.defs
185 .insert(SymName::Type(n.str()), tydef.syntax().handle())
186 }),
187 });
188 }
189 StmtKind::Typeclass(tyclass) => {
190 if let Some(name) = tyclass.name() {
191 top.defs
192 .insert(SymName::Type(name.str()), tyclass.syntax().handle());
193 };
194 }
195 StmtKind::ExprOrBind(_) => (),
196 StmtKind::Instance(_) => (),
197 StmtKind::Method(_) => (),
198 }
199 }
200
201 top
202 }
203
204 pub fn root(&self) -> view::Package<'_> {
205 view::Package::try_cast(self.cst.root()).expect("CST root node is not 'Package'")
206 }
207
208 pub fn compute_globals(&self, proj: &Project) -> HashMap<SymName, (PkgHandle, NodeHandle)> {
209 let pkg = self.ctx.pkg;
210 let mut defs: HashMap<SymName, (PkgHandle, NodeHandle)> = HashMap::new();
211
212 for imported in &self.top.imports {
213 let exports = proj.exports.get_as(proj, *imported, pkg);
214 defs.extend(exports.defs.iter().map(|(k, v)| (k.clone(), *v)));
215 }
216
217 defs.extend(self.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
218 proj.get_pkg(pkg)
219 .name
220 .clone()
221 .map(|name| defs.insert(SymName::Pkg(name), (pkg, self.root().syntax().handle())));
222
223 defs
224 }
225}