Skip to content

Commit f2cc6a8

Browse files
committed
[ci skip] Setup the REPL and the test runner
1 parent 1478b97 commit f2cc6a8

33 files changed

Lines changed: 1520 additions & 338 deletions

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

analyzer/src/hir.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::Reef;
55
use ast::call::{RedirFd, RedirOp};
66
use ast::value::LiteralValue;
77
use context::source::Span;
8-
use std::collections::hash_map::Values;
98
use std::collections::HashMap;
109
use std::path::PathBuf;
1110
use std::rc::Rc;
@@ -175,6 +174,7 @@ pub enum ExprKind {
175174
Capture(Vec<TypedExpr>),
176175
Substitute(Substitute),
177176
Subprocess(Subprocess),
177+
Cast(Box<TypedExpr>),
178178

179179
Continue,
180180
Break,
@@ -246,7 +246,7 @@ impl Module {
246246
impl Reef {
247247
pub fn group_by_content(&self) -> ContentIterator {
248248
ContentIterator {
249-
inner: self.hir.values(),
249+
inner: self.hir.as_slice().iter(),
250250
}
251251
}
252252
}
@@ -267,7 +267,7 @@ pub struct EncodableContent<'a> {
267267
}
268268

269269
pub struct ContentIterator<'a> {
270-
inner: Values<'a, PathBuf, Module>,
270+
inner: std::slice::Iter<'a, Module>,
271271
}
272272

273273
impl<'a> Iterator for ContentIterator<'a> {

analyzer/src/hoist.rs

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ use crate::typing::user::{
66
lookup_builtin_type, TypeId, UserType, ERROR_TYPE, STRING_TYPE, UNIT_TYPE,
77
};
88
use crate::typing::{Parameter, TypeChecker, TypeErrorKind};
9-
use crate::{Reef, SourceLocation, TypeError};
9+
use crate::{Reef, SourceLocation, TypeError, UnitKey};
1010
use ast::function::{FunctionDeclaration, FunctionParameter};
1111
use ast::r#struct::{StructDeclaration, StructImpl};
1212
use ast::r#type::Type;
13-
use ast::r#use::{Import, ImportedSymbol, InclusionPathItem};
13+
use ast::r#use::{Import, ImportList, ImportedSymbol, InclusionPathItem};
1414
use ast::variable::TypedVariable;
1515
use ast::Expr;
1616
use context::source::{SourceSegmentHolder, Span};
17-
use parser::Root;
18-
use std::collections::HashMap;
19-
use std::ffi::OsString;
17+
use std::collections::{HashMap, HashSet};
18+
use std::ffi::{OsStr, OsString};
2019
use std::path::{Path, PathBuf};
2120

2221
/// Places functions and types at the top level of the symbol table.
@@ -34,15 +33,26 @@ use std::path::{Path, PathBuf};
3433
/// This step performs multiple passes where types are added to the symbol table, and then their
3534
/// fields are added to the type. A third pass is done to hoist functions, now that all parameters
3635
/// and return types may be known.
36+
///
37+
/// # Exploration order
38+
///
39+
/// The hoisting phase creates an identity for each module and outlines the dependencies between
40+
/// them. It does then compute a satisfying order to analyse each module, one at a time, that should
41+
/// not have cyclic dependencies for variables.
3742
pub(super) fn hoist_files(
3843
foreign: &HashMap<OsString, ModuleTree>,
3944
reef: &mut Reef,
4045
checker: &mut TypeChecker,
46+
mut keys: Vec<UnitKey>,
4147
) -> HoistingResult {
4248
let mut errors = Vec::<TypeError>::new();
4349
let mut graph = HashMap::new();
44-
for (path, root) in &reef.files {
45-
let mut table = SymbolTable::new(path.clone());
50+
for key @ UnitKey { path, .. } in &keys {
51+
let root = reef.files.get(key).expect("unit should exist");
52+
let mut table = reef
53+
.symbols
54+
.remove(path)
55+
.unwrap_or_else(|| SymbolTable::new(path.clone()));
4656
let mut exports = reef.exports.take_exports(path); // This is not problematic if the export is not found, but it shouldn't happen
4757
let modules = ModuleView::new(&reef.exports, foreign);
4858
let mut deps = Dependencies {
@@ -63,17 +73,24 @@ pub(super) fn hoist_files(
6373
reef.symbols.insert(path.clone(), table);
6474
}
6575

66-
let mut sorted: Vec<PathBuf> = Vec::new();
76+
let mut sorted: Vec<UnitKey> = Vec::new();
6777
let mut frontier: Vec<PathBuf> = graph
6878
.keys()
6979
.filter(|module| graph.values().all(|deps| !deps.contains(module)))
7080
.cloned()
7181
.collect();
82+
let mut seen: HashSet<PathBuf> = frontier.iter().cloned().collect(); // Deduplicate when the same file is imported multiple times
7283
while let Some(module) = frontier.pop() {
73-
sorted.push(module.clone());
84+
let key = keys.swap_remove(
85+
keys.iter()
86+
.position(|key| key.path == module)
87+
.expect("module should exist"),
88+
);
89+
sorted.push(key);
7490
if let Some(requires) = graph.remove(&module) {
7591
for require in requires {
76-
if !graph.values().any(|deps| deps.contains(&require)) {
92+
if !seen.contains(&require) && !graph.values().any(|deps| deps.contains(&require)) {
93+
seen.insert(require.clone());
7794
frontier.push(require);
7895
}
7996
}
@@ -99,7 +116,7 @@ pub(super) fn hoist_files(
99116

100117
pub(crate) struct HoistingResult {
101118
pub(crate) errors: Vec<TypeError>,
102-
pub(crate) sorted: Vec<PathBuf>,
119+
pub(crate) sorted: Vec<UnitKey>,
103120
}
104121

105122
struct Dependencies<'a> {
@@ -108,12 +125,12 @@ struct Dependencies<'a> {
108125
}
109126

110127
fn hoist_type_names(
111-
root: &Root,
128+
root: &[Expr],
112129
checker: &mut TypeChecker,
113130
table: &mut SymbolTable,
114131
exports: &mut [Export],
115132
) {
116-
for expr in &root.expressions {
133+
for expr in root.iter() {
117134
if let Expr::StructDeclaration(StructDeclaration {
118135
name,
119136
segment: span,
@@ -139,14 +156,14 @@ fn hoist_type_names(
139156
}
140157

141158
fn hoist_functions(
142-
root: &Root,
159+
root: &[Expr],
143160
checker: &mut TypeChecker,
144161
table: &mut SymbolTable,
145162
exports: &mut [Export],
146163
deps: &mut Dependencies,
147164
errors: &mut Vec<TypeError>,
148165
) {
149-
for expr in &root.expressions {
166+
for expr in root.iter() {
150167
match expr {
151168
Expr::FunctionDeclaration(fn_decl) => {
152169
hoist_fn_decl(fn_decl, None, checker, table, exports, errors);
@@ -164,6 +181,9 @@ fn hoist_functions(
164181
alias,
165182
segment: span,
166183
}) => {
184+
if matches!(path.first(), Some(InclusionPathItem::Symbol(_))) {
185+
return; // Exclude inter-reefs dependencies
186+
}
167187
let (last, rest) = path.split_last().expect("at least one item");
168188
if let Some(module) = deps.modules.get_direct(rest) {
169189
for export in &module.exports {
@@ -192,10 +212,55 @@ fn hoist_functions(
192212
}
193213
}
194214
}
195-
Import::AllIn(path, _) => todo!(),
215+
Import::AllIn(path, _) => {
216+
if matches!(path.first(), Some(InclusionPathItem::Symbol(_))) {
217+
return; // Exclude inter-reefs dependencies
218+
}
219+
if let Some(module) = deps.modules.get_direct(path) {
220+
for export in &module.exports {
221+
table.insert_remote(export.name.clone(), import.segment(), export);
222+
}
223+
}
224+
}
196225
Import::Environment(_) => {}
197-
Import::List(list) => {
198-
todo!()
226+
Import::List(ImportList {
227+
root,
228+
imports,
229+
segment: span,
230+
}) => {
231+
if matches!(root.first(), Some(InclusionPathItem::Symbol(_))) {
232+
return; // Exclude inter-reefs dependencies
233+
}
234+
let base = root
235+
.iter()
236+
.skip_while(|item| matches!(item, InclusionPathItem::Reef(_)))
237+
.map(|item| item.name())
238+
.collect::<PathBuf>();
239+
if let Some(mut module) = deps.modules.get_direct(root) {
240+
for import in imports {
241+
match import {
242+
Import::Symbol(symbol) => {
243+
let mut path = base.clone();
244+
for part in symbol.path.iter() {
245+
if let InclusionPathItem::Symbol(ident) = part {
246+
if let Some(tree) =
247+
module.get(OsStr::new(ident.value.as_str()))
248+
{
249+
path.push(ident.value.as_str());
250+
module = tree;
251+
} else {
252+
deps.requires.push(path);
253+
break;
254+
}
255+
}
256+
}
257+
}
258+
Import::AllIn(_, _) => {}
259+
Import::Environment(_) => {}
260+
Import::List(_) => {}
261+
}
262+
}
263+
}
199264
}
200265
}
201266
}
@@ -214,6 +279,7 @@ fn hoist_fn_decl(
214279
name,
215280
type_parameters,
216281
parameters,
282+
body,
217283
return_type,
218284
..
219285
}: &FunctionDeclaration,
@@ -271,15 +337,37 @@ fn hoist_fn_decl(
271337
}
272338
None => UNIT_TYPE,
273339
};
274-
let mut fqn = table.path.clone();
275-
fqn.push(name.value.to_string());
340+
table.exit_scope();
341+
let fqn = if let Some(current_ty) = current_ty {
342+
let UserType::Parametrized { schema, .. } = checker.types[current_ty] else {
343+
panic!(
344+
"the current type should be a struct, got {:?}",
345+
checker.types[current_ty]
346+
);
347+
};
348+
let Schema {
349+
name: ref type_name,
350+
..
351+
} = checker.registry[schema];
352+
let mut fqn = PathBuf::from(type_name);
353+
fqn.push(name.value.to_string());
354+
fqn
355+
} else {
356+
let mut fqn = table.path.clone();
357+
fqn.push(name.value.to_string());
358+
fqn
359+
};
276360
let function = checker.registry.define_function(Function {
277361
declared_at: table.path.clone(),
278362
fqn,
279363
generic_variables,
280364
param_types,
281365
return_type,
282-
kind: FunctionKind::Function,
366+
kind: if body.is_some() {
367+
FunctionKind::Function
368+
} else {
369+
FunctionKind::Intrinsic
370+
},
283371
});
284372
let function_type = checker.types.alloc(UserType::Function(function));
285373
match current_ty {
@@ -548,12 +636,15 @@ mod tests {
548636

549637
fn hoist_files(fs: MemoryFilesystem, entrypoint: &str) -> Vec<TypeError> {
550638
let mut reef = Reef::new(OsString::from("test"));
551-
assert_eq!(
552-
import_multi(&mut reef, &fs, entrypoint),
553-
[],
554-
"no import errors should be found"
555-
);
556-
super::hoist_files(&mut HashMap::new(), &mut reef, &mut TypeChecker::default()).errors
639+
let import_result = import_multi(&mut reef, &fs, entrypoint);
640+
assert_eq!(import_result.errors, [], "no import errors should be found");
641+
super::hoist_files(
642+
&mut HashMap::new(),
643+
&mut reef,
644+
&mut TypeChecker::default(),
645+
import_result.keys,
646+
)
647+
.errors
557648
}
558649

559650
fn hoist(source: &str) -> Vec<TypeError> {

0 commit comments

Comments
 (0)