[fine] Lifetime garbage, big refactor
So it turns out that I can't hold `&str` in token because it makes it impossible to encapsulate a source file in the larger context- self referential structure problems again. Everything gets rebuilt so that the source can be passed through. While we're at it, more things become Rc<> because, man..... life it too short. Semantics in particular has become a giant hub of the module state: we can basically just hold an Rc<Semantics> and have everything we could possibly want to know about a source file, computed lazily if necessary.
This commit is contained in:
parent
d5059dd450
commit
2dbdbb3957
7 changed files with 502 additions and 329 deletions
|
|
@ -149,6 +149,9 @@ pub enum Type {
|
|||
|
||||
// An alternate is one or another type.
|
||||
Alternate(Box<[Type]>),
|
||||
|
||||
// A module of some kind. What module?
|
||||
Module(Rc<str>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
|
|
@ -163,19 +166,20 @@ impl Type {
|
|||
match self {
|
||||
Type::Error => 0,
|
||||
Type::Unreachable => 1,
|
||||
Type::Assignment(_) => 2,
|
||||
Type::TypeVariable(_) => 3,
|
||||
Type::Assignment(..) => 2,
|
||||
Type::TypeVariable(..) => 3,
|
||||
Type::Nothing => 4,
|
||||
Type::F64 => 5,
|
||||
Type::I64 => 6,
|
||||
Type::String => 7,
|
||||
Type::Bool => 8,
|
||||
Type::Function(_, _) => 9,
|
||||
Type::Method(_, _, _) => 10,
|
||||
Type::List(_) => 11,
|
||||
Type::Class(_, _) => 12,
|
||||
Type::Object(_, _) => 13,
|
||||
Type::Alternate(_) => 14,
|
||||
Type::Function(..) => 9,
|
||||
Type::Method(..) => 10,
|
||||
Type::List(..) => 11,
|
||||
Type::Class(..) => 12,
|
||||
Type::Object(..) => 13,
|
||||
Type::Alternate(..) => 14,
|
||||
Type::Module(..) => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,6 +246,7 @@ impl fmt::Display for Type {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Module(name) => write!(f, "module {}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -335,6 +340,9 @@ pub enum Declaration {
|
|||
Class {
|
||||
declaration: TreeRef, //?
|
||||
},
|
||||
Import {
|
||||
declaration: TreeRef,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Environment {
|
||||
|
|
@ -383,8 +391,8 @@ impl Environment {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, token: &Token, t: TreeRef) -> Option<Declaration> {
|
||||
self.insert_name(token.as_str().into(), t)
|
||||
pub fn insert(&mut self, token: &str, t: TreeRef) -> Option<Declaration> {
|
||||
self.insert_name(token.into(), t)
|
||||
}
|
||||
|
||||
pub fn insert_name(&mut self, name: Box<str>, t: TreeRef) -> Option<Declaration> {
|
||||
|
|
@ -400,14 +408,14 @@ impl Environment {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn bind(&self, token: &Token) -> Option<&Declaration> {
|
||||
if let Some(decl) = self.declarations.get(token.as_str()) {
|
||||
pub fn bind(&self, token: &str) -> Option<&Declaration> {
|
||||
if let Some(decl) = self.declarations.get(token) {
|
||||
return Some(decl);
|
||||
}
|
||||
|
||||
let mut current = &self.parent;
|
||||
while let Some(env) = current {
|
||||
if let Some(decl) = env.declarations.get(token.as_str()) {
|
||||
if let Some(decl) = env.declarations.get(token) {
|
||||
return Some(decl);
|
||||
}
|
||||
current = &env.parent;
|
||||
|
|
@ -597,11 +605,10 @@ enum Incremental<T> {
|
|||
Complete(T),
|
||||
}
|
||||
|
||||
pub struct Semantics<'a> {
|
||||
// TODO: Do I really want my own copy here? Should we standardize on Arc
|
||||
// or Rc or some other nice sharing mechanism?
|
||||
syntax_tree: &'a SyntaxTree<'a>,
|
||||
lines: &'a Lines,
|
||||
pub struct Semantics {
|
||||
source: Rc<str>,
|
||||
syntax_tree: Rc<SyntaxTree>,
|
||||
lines: Rc<Lines>,
|
||||
|
||||
// Instead of physical parents, this is the set of *logical* parents.
|
||||
// This is what is used for binding.
|
||||
|
|
@ -615,17 +622,18 @@ pub struct Semantics<'a> {
|
|||
classes: RefCell<Vec<Incremental<ClassRef>>>,
|
||||
}
|
||||
|
||||
impl<'a> Semantics<'a> {
|
||||
pub fn new(tree: &'a SyntaxTree<'a>, lines: &'a Lines) -> Self {
|
||||
impl Semantics {
|
||||
pub fn new(source: Rc<str>, tree: Rc<SyntaxTree>, lines: Rc<Lines>) -> Self {
|
||||
let mut logical_parents = vec![None; tree.len()];
|
||||
if let Some(root) = tree.root() {
|
||||
set_logical_parents(&mut logical_parents, tree, root, None);
|
||||
set_logical_parents(&mut logical_parents, &tree, root, None);
|
||||
}
|
||||
|
||||
let root_environment = Environment::new(None, Location::Module);
|
||||
|
||||
let mut semantics = Semantics {
|
||||
syntax_tree: tree,
|
||||
source,
|
||||
syntax_tree: tree.clone(),
|
||||
lines,
|
||||
logical_parents,
|
||||
errors: RefCell::new(vec![]),
|
||||
|
|
@ -645,8 +653,16 @@ impl<'a> Semantics<'a> {
|
|||
semantics
|
||||
}
|
||||
|
||||
pub fn tree(&self) -> &SyntaxTree<'a> {
|
||||
&self.syntax_tree
|
||||
pub fn source(&self) -> Rc<str> {
|
||||
self.source.clone()
|
||||
}
|
||||
|
||||
pub fn tree(&self) -> Rc<SyntaxTree> {
|
||||
self.syntax_tree.clone()
|
||||
}
|
||||
|
||||
pub fn lines(&self) -> Rc<Lines> {
|
||||
self.lines.clone()
|
||||
}
|
||||
|
||||
pub fn snapshot_errors(&self) -> Vec<Error> {
|
||||
|
|
@ -666,16 +682,6 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn report_error<T>(&self, position: usize, error: T)
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
let (line, col) = self.lines.position(position);
|
||||
self.errors
|
||||
.borrow_mut()
|
||||
.push(Error::new(line, col, error.to_string()));
|
||||
}
|
||||
|
||||
fn report_error_span<T>(&self, start: usize, end: usize, error: T)
|
||||
where
|
||||
T: ToString,
|
||||
|
|
@ -687,7 +693,7 @@ impl<'a> Semantics<'a> {
|
|||
.push(Error::new_spanned(start, end, error.to_string()));
|
||||
}
|
||||
|
||||
fn report_error_tree<T>(&self, tree: &Tree<'a>, error: T)
|
||||
fn report_error_tree<T>(&self, tree: &Tree, error: T)
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
|
|
@ -702,9 +708,6 @@ impl<'a> Semantics<'a> {
|
|||
self.report_error_span(tree.start_pos, tree.end_pos, error)
|
||||
}
|
||||
|
||||
// pub fn lvalue_declaration(&self, t: TreeRef) -> Option<&Declaration> {
|
||||
// }
|
||||
|
||||
fn gather_errors(&mut self, tree: TreeRef) {
|
||||
let mut stack = vec![tree];
|
||||
while let Some(tr) = stack.pop() {
|
||||
|
|
@ -713,7 +716,7 @@ impl<'a> Semantics<'a> {
|
|||
match child {
|
||||
Child::Token(t) => {
|
||||
if t.kind == TokenKind::Error {
|
||||
self.report_error(t.start, t.as_str());
|
||||
self.report_error_span(t.start(), t.end(), t.as_str(&self.source));
|
||||
}
|
||||
}
|
||||
Child::Tree(t) => stack.push(*t),
|
||||
|
|
@ -779,13 +782,16 @@ impl<'a> Semantics<'a> {
|
|||
};
|
||||
|
||||
let existing = environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
name.as_str(&self.source).into(),
|
||||
Declaration::Function { declaration: *t },
|
||||
);
|
||||
if existing.is_some() {
|
||||
self.report_error_tree(
|
||||
ct,
|
||||
format!("duplicate definition of function '{name}'"),
|
||||
format!(
|
||||
"duplicate definition of function '{}'",
|
||||
name.as_str(&self.source)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -799,43 +805,63 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
fn environment_of_file(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
let mut environment = Environment::new(Some(parent), Location::Module);
|
||||
|
||||
for child in tree.children.iter() {
|
||||
match child {
|
||||
Child::Tree(t) => {
|
||||
let ct = &self.syntax_tree[*t];
|
||||
let binding = match ct.kind {
|
||||
TreeKind::FunctionDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
let Child::Tree(t) = child else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let declaration = Declaration::Function { declaration: *t };
|
||||
Some(("function", name, declaration))
|
||||
}
|
||||
TreeKind::ClassDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let declaration = Declaration::Class { declaration: *t };
|
||||
Some(("class", name, declaration))
|
||||
}
|
||||
_ => None,
|
||||
let ct = &self.syntax_tree[*t];
|
||||
let binding = match ct.kind {
|
||||
TreeKind::FunctionDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some((what, name, declaration)) = binding {
|
||||
let existing = environment
|
||||
.declarations
|
||||
.insert(name.as_str().into(), declaration);
|
||||
if existing.is_some() {
|
||||
self.report_error_tree(
|
||||
ct,
|
||||
format!("duplicate definition of {what} '{name}'"),
|
||||
);
|
||||
}
|
||||
if name.kind != TokenKind::Identifier {
|
||||
continue;
|
||||
}
|
||||
|
||||
let declaration = Declaration::Function { declaration: *t };
|
||||
Some(("function", name, declaration))
|
||||
}
|
||||
TreeKind::ClassDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
if name.kind != TokenKind::Identifier {
|
||||
continue;
|
||||
}
|
||||
|
||||
let declaration = Declaration::Class { declaration: *t };
|
||||
Some(("class", name, declaration))
|
||||
}
|
||||
TreeKind::Import => {
|
||||
let Some(name) = ct.nth_token(3) else {
|
||||
continue;
|
||||
};
|
||||
if name.kind != TokenKind::Identifier {
|
||||
continue;
|
||||
}
|
||||
|
||||
let declaration = Declaration::Import { declaration: *t };
|
||||
Some(("import", name, declaration))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((what, name, declaration)) = binding {
|
||||
let existing = environment
|
||||
.declarations
|
||||
.insert(name.as_str(&self.source).into(), declaration);
|
||||
if existing.is_some() {
|
||||
self.report_error_tree(
|
||||
ct,
|
||||
format!(
|
||||
"duplicate definition of {what} '{}'",
|
||||
name.as_str(&self.source)
|
||||
),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -861,7 +887,7 @@ impl<'a> Semantics<'a> {
|
|||
};
|
||||
|
||||
let mut environment = Environment::new(Some(parent), location);
|
||||
environment.insert(name, declaration);
|
||||
environment.insert(name.as_str(&self.source), declaration);
|
||||
|
||||
EnvironmentRef::new(environment)
|
||||
}
|
||||
|
|
@ -878,7 +904,10 @@ impl<'a> Semantics<'a> {
|
|||
match param.kind {
|
||||
TreeKind::SelfParameter => {
|
||||
let param_name = param.nth_token(0).unwrap();
|
||||
if environment.insert(param_name, *ct).is_some() {
|
||||
if environment
|
||||
.insert(param_name.as_str(&self.source), *ct)
|
||||
.is_some()
|
||||
{
|
||||
self.report_error_tree(
|
||||
param,
|
||||
format!("duplicate definition of self parameter"),
|
||||
|
|
@ -895,10 +924,11 @@ impl<'a> Semantics<'a> {
|
|||
continue;
|
||||
};
|
||||
|
||||
if environment.insert(param_name, *ct).is_some() {
|
||||
let param_str = param_name.as_str(&self.source);
|
||||
if environment.insert(param_str, *ct).is_some() {
|
||||
self.report_error_tree(
|
||||
param,
|
||||
format!("duplicate definition of parameter '{param_name}'"),
|
||||
format!("duplicate definition of parameter '{param_str}'"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -920,7 +950,7 @@ impl<'a> Semantics<'a> {
|
|||
};
|
||||
|
||||
let mut environment = Environment::new(Some(parent), Location::Local);
|
||||
environment.insert(id, it);
|
||||
environment.insert(id.as_str(&self.source), it);
|
||||
EnvironmentRef::new(environment)
|
||||
}
|
||||
|
||||
|
|
@ -928,7 +958,7 @@ impl<'a> Semantics<'a> {
|
|||
assert_eq!(tree.kind, TreeKind::IsExpression);
|
||||
|
||||
// The environment of an `is` expression is the environment produced by the pattern.
|
||||
let Some(pattern) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::Pattern) else {
|
||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||
// Should really have a pattern in there; otherwise there was a
|
||||
// parse error, don't make more trouble.
|
||||
return Environment::error();
|
||||
|
|
@ -950,7 +980,7 @@ impl<'a> Semantics<'a> {
|
|||
assert_eq!(tree.kind, TreeKind::MatchArm);
|
||||
|
||||
// The environment of a `match arm` expression is the environment produced by the pattern.
|
||||
let Some(pattern) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::Pattern) else {
|
||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||
// Should really have a pattern in there; otherwise there was a
|
||||
// parse error, don't make more trouble.
|
||||
return Environment::error();
|
||||
|
|
@ -989,7 +1019,7 @@ impl<'a> Semantics<'a> {
|
|||
) -> EnvironmentRef {
|
||||
assert_eq!(tree.kind, TreeKind::Pattern);
|
||||
|
||||
let Some(binding) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::VariableBinding)
|
||||
let Some(binding) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::VariableBinding)
|
||||
else {
|
||||
// No binding, no new environment.
|
||||
return parent;
|
||||
|
|
@ -999,7 +1029,7 @@ impl<'a> Semantics<'a> {
|
|||
};
|
||||
|
||||
let is_wildcard = tree
|
||||
.child_of_kind(self.syntax_tree, TreeKind::WildcardPattern)
|
||||
.child_of_kind(&self.syntax_tree, TreeKind::WildcardPattern)
|
||||
.is_some();
|
||||
|
||||
let variable_decl = if is_wildcard {
|
||||
|
|
@ -1010,7 +1040,7 @@ impl<'a> Semantics<'a> {
|
|||
} else {
|
||||
// Otherwise the binding is to the type expression which must
|
||||
// match for the variable to have a value.
|
||||
let Some(type_expr) = tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)
|
||||
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
||||
else {
|
||||
return Environment::error();
|
||||
};
|
||||
|
|
@ -1020,7 +1050,7 @@ impl<'a> Semantics<'a> {
|
|||
// TODO: This binding should be un-assignable! Don't assign to this!
|
||||
|
||||
let mut env = Environment::new(Some(parent), Location::Local);
|
||||
env.insert(variable, variable_decl);
|
||||
env.insert(variable.as_str(&self.source), variable_decl);
|
||||
EnvironmentRef::new(env)
|
||||
}
|
||||
|
||||
|
|
@ -1044,11 +1074,14 @@ impl<'a> Semantics<'a> {
|
|||
let tree = &self.syntax_tree[t];
|
||||
assert_eq!(tree.kind, TreeKind::ClassDecl);
|
||||
|
||||
let name = tree.nth_token(1).map(|t| t.as_str()).unwrap_or("<??>");
|
||||
let name = tree
|
||||
.nth_token(1)
|
||||
.map(|t| t.as_str(&self.source))
|
||||
.unwrap_or("<??>");
|
||||
|
||||
// Fields
|
||||
let mut fields = Vec::new();
|
||||
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
|
||||
for field in tree.children_of_kind(&self.syntax_tree, TreeKind::FieldDecl) {
|
||||
let f = &self.syntax_tree[field];
|
||||
if let Some(field_name) = f.nth_token(0) {
|
||||
let field_type = f
|
||||
|
|
@ -1056,7 +1089,7 @@ impl<'a> Semantics<'a> {
|
|||
.map(|t| self.type_of(t))
|
||||
.unwrap_or(Type::Error);
|
||||
fields.push(FieldDecl {
|
||||
name: field_name.as_str().into(),
|
||||
name: field_name.as_str(&self.source).into(),
|
||||
declaration: field,
|
||||
field_type,
|
||||
});
|
||||
|
|
@ -1065,7 +1098,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
// Methods
|
||||
let mut methods = Vec::new();
|
||||
for method in tree.children_of_kind(self.syntax_tree, TreeKind::FunctionDecl) {
|
||||
for method in tree.children_of_kind(&self.syntax_tree, TreeKind::FunctionDecl) {
|
||||
let m = &self.syntax_tree[method];
|
||||
if let Some(method_name) = m.nth_token(1) {
|
||||
// TODO: Check to see if it is actually a method, or if it is a static function.
|
||||
|
|
@ -1073,7 +1106,7 @@ impl<'a> Semantics<'a> {
|
|||
match decl_type {
|
||||
Type::Method(..) => {
|
||||
methods.push(MethodDecl {
|
||||
name: method_name.as_str().into(),
|
||||
name: method_name.as_str(&self.source).into(),
|
||||
decl_type,
|
||||
declaration: method,
|
||||
is_static: false,
|
||||
|
|
@ -1082,7 +1115,7 @@ impl<'a> Semantics<'a> {
|
|||
_ => {
|
||||
// TODO: Default to method or static?
|
||||
methods.push(MethodDecl {
|
||||
name: method_name.as_str().into(),
|
||||
name: method_name.as_str(&self.source).into(),
|
||||
decl_type,
|
||||
declaration: method,
|
||||
is_static: true,
|
||||
|
|
@ -1287,6 +1320,7 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
TreeKind::WhileStatement => self.type_of_while(tree),
|
||||
TreeKind::Import => self.type_of_import(tree),
|
||||
|
||||
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
|
||||
};
|
||||
|
|
@ -1313,8 +1347,9 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
// This is dumb and should be punished, probably.
|
||||
(_, Type::Unreachable) => {
|
||||
self.report_error(
|
||||
op.start,
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
"cannot apply a unary operator to something that doesn't yield a value",
|
||||
);
|
||||
Some(Type::Error)
|
||||
|
|
@ -1324,11 +1359,12 @@ impl<'a> Semantics<'a> {
|
|||
(_, Type::Error) => Some(Type::Error),
|
||||
|
||||
(_, arg_type) => {
|
||||
self.report_error(
|
||||
op.start,
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
format!(
|
||||
"cannot apply unary operator '{}' to value of type {}",
|
||||
op.as_str(),
|
||||
op.as_str(&self.source),
|
||||
arg_type
|
||||
),
|
||||
);
|
||||
|
|
@ -1372,16 +1408,24 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
// This is dumb and should be punished, probably.
|
||||
(_, _, Type::Unreachable) => {
|
||||
self.report_error(
|
||||
op.start,
|
||||
format!("cannot apply '{op}' to an argument that doesn't yield a value (on the right)"),
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
format!(
|
||||
"cannot apply '{}' to an argument that doesn't yield a value (on the right)",
|
||||
op.as_str(&self.source)
|
||||
),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
(_, Type::Unreachable, _) => {
|
||||
self.report_error(
|
||||
op.start,
|
||||
format!("cannot apply '{op}' to an argument that doesn't yield a value (on the left)"),
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
format!(
|
||||
"cannot apply '{}' to an argument that doesn't yield a value (on the left)",
|
||||
op.as_str(&self.source)
|
||||
),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
|
|
@ -1395,9 +1439,13 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
// Missed the whole table, it must be an error.
|
||||
(_, left_type, right_type) => {
|
||||
self.report_error(
|
||||
op.start,
|
||||
format!("cannot apply binary operator '{op}' to expressions of type '{left_type}' (on the left) and '{right_type}' (on the right)"),
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
format!(
|
||||
"cannot apply binary operator '{}' to expressions of type '{left_type}' (on the left) and '{right_type}' (on the right)",
|
||||
op.as_str(&self.source)
|
||||
),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
|
|
@ -1420,7 +1468,7 @@ impl<'a> Semantics<'a> {
|
|||
let declaration = match tree.kind {
|
||||
// TODO: Assign to list access
|
||||
TreeKind::Identifier => {
|
||||
let id = tree.nth_token(0)?;
|
||||
let id = tree.nth_token(0)?.as_str(&self.source);
|
||||
environment = self.environment_of(left_tree);
|
||||
match environment.bind(id) {
|
||||
Some(decl) => decl,
|
||||
|
|
@ -1433,7 +1481,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
TreeKind::MemberAccess => {
|
||||
let id = tree.nth_token(2)?;
|
||||
let id = tree.nth_token(2)?.as_str(&self.source);
|
||||
let typ = self.type_of(tree.nth_tree(0)?);
|
||||
environment = self.member_environment(left_tree, &typ);
|
||||
match environment.bind(id) {
|
||||
|
|
@ -1471,6 +1519,13 @@ impl<'a> Semantics<'a> {
|
|||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
Declaration::Import { .. } => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a new value to an imported module",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = environment;
|
||||
|
|
@ -1489,8 +1544,9 @@ impl<'a> Semantics<'a> {
|
|||
} else if self.can_convert(&right_type, &left_type) {
|
||||
Some(Type::Assignment(Box::new(left_type)))
|
||||
} else {
|
||||
self.report_error(
|
||||
op.start,
|
||||
self.report_error_span(
|
||||
op.start(),
|
||||
op.end(),
|
||||
format!("cannot assign a value of type '{right_type}' to type '{left_type}'"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
|
|
@ -1506,15 +1562,15 @@ impl<'a> Semantics<'a> {
|
|||
assert_eq!(tree.kind, TreeKind::TypeIdentifier);
|
||||
|
||||
// TODO: This will *clearly* need to get better.
|
||||
let token = tree.nth_token(0)?;
|
||||
match token.as_str() {
|
||||
let token = tree.nth_token(0)?.as_str(&self.source);
|
||||
match token {
|
||||
"f64" => Some(Type::F64),
|
||||
"string" => Some(Type::String),
|
||||
"bool" => Some(Type::Bool),
|
||||
"nothing" => Some(Type::Nothing),
|
||||
"list" => {
|
||||
let args =
|
||||
tree.child_tree_of_kind(self.syntax_tree, TreeKind::TypeParameterList)?;
|
||||
tree.child_tree_of_kind(&self.syntax_tree, TreeKind::TypeParameterList)?;
|
||||
let mut arg_types: Vec<_> = args.child_trees().map(|t| self.type_of(t)).collect();
|
||||
|
||||
if arg_types.len() != 1 {
|
||||
|
|
@ -1544,6 +1600,13 @@ impl<'a> Semantics<'a> {
|
|||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
Some(Declaration::Import { .. }) => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{token}' is an imported module and cannot be used as a type"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
|
|
@ -1609,7 +1672,10 @@ impl<'a> Semantics<'a> {
|
|||
TokenKind::Number => Type::F64,
|
||||
TokenKind::String => Type::String,
|
||||
TokenKind::True | TokenKind::False => Type::Bool,
|
||||
_ => panic!("the token {tok} doesn't have a type!"),
|
||||
_ => panic!(
|
||||
"the token {} doesn't have a type!",
|
||||
tok.as_str(&self.source)
|
||||
),
|
||||
};
|
||||
Some(pig)
|
||||
}
|
||||
|
|
@ -1766,9 +1832,14 @@ impl<'a> Semantics<'a> {
|
|||
return Some(Type::Error);
|
||||
}
|
||||
|
||||
let Some(declaration) = env.bind(id) else {
|
||||
let id_str = id.as_str(&self.source);
|
||||
let Some(declaration) = env.bind(id_str) else {
|
||||
if !env.is_error {
|
||||
self.report_error(id.start, format!("'{typ}' has no member {id}"));
|
||||
self.report_error_span(
|
||||
id.start(),
|
||||
id.end(),
|
||||
format!("'{typ}' has no member {id_str}"),
|
||||
);
|
||||
}
|
||||
return Some(Type::Error);
|
||||
};
|
||||
|
|
@ -1786,6 +1857,10 @@ impl<'a> Semantics<'a> {
|
|||
let class = self.class_of(*ct);
|
||||
class.static_env.clone()
|
||||
}
|
||||
// Type::Module(_name) => {
|
||||
// // Woof. Would like to bind this now.
|
||||
// todo!();
|
||||
// }
|
||||
Type::Error => return Environment::error(),
|
||||
_ => {
|
||||
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
||||
|
|
@ -1820,7 +1895,7 @@ impl<'a> Semantics<'a> {
|
|||
fn type_of_identifier(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::Identifier);
|
||||
|
||||
let id = tree.nth_token(0)?;
|
||||
let id = tree.nth_token(0)?.as_str(&self.source);
|
||||
let environment = self.environment_of(t);
|
||||
if let Some(declaration) = environment.bind(id) {
|
||||
return Some(self.type_of_declaration(t, declaration));
|
||||
|
|
@ -1836,6 +1911,7 @@ impl<'a> Semantics<'a> {
|
|||
match declaration {
|
||||
Declaration::Variable { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::Function { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::Import { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
|
|
@ -1867,7 +1943,7 @@ impl<'a> Semantics<'a> {
|
|||
fn type_of_self_reference(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::SelfReference);
|
||||
|
||||
let id = tree.nth_token(0)?;
|
||||
let id = tree.nth_token(0)?.as_str(&self.source);
|
||||
let environment = self.environment_of(t);
|
||||
if let Some(declaration) = environment.bind(id) {
|
||||
return Some(match declaration {
|
||||
|
|
@ -1886,7 +1962,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
fn type_of_function_decl(&self, tree: &Tree) -> Option<Type> {
|
||||
let param_list = tree.child_tree_of_kind(self.syntax_tree, TreeKind::ParamList)?;
|
||||
let param_list = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::ParamList)?;
|
||||
|
||||
// NOTE: The methodness here is determined by the presence of a self
|
||||
// parameter, even if that parameter is incorrect (e.g., this
|
||||
|
|
@ -1905,7 +1981,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let return_type = match tree.child_of_kind(self.syntax_tree, TreeKind::ReturnType) {
|
||||
let return_type = match tree.child_of_kind(&self.syntax_tree, TreeKind::ReturnType) {
|
||||
Some(t) => self.type_of(t),
|
||||
None => Type::Nothing,
|
||||
};
|
||||
|
|
@ -1919,7 +1995,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
fn type_of_parameter(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::Parameter);
|
||||
match tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression) {
|
||||
match tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression) {
|
||||
Some(t) => Some(self.type_of(t)),
|
||||
None => {
|
||||
self.report_error_tree(tree, format!("the parameter is missing a type"));
|
||||
|
|
@ -1991,7 +2067,7 @@ impl<'a> Semantics<'a> {
|
|||
// The details of a class are computed lazily, but this is enough of
|
||||
// a belly-button.
|
||||
let name = tree.nth_token(1)?;
|
||||
Some(Type::Object(t, name.as_str().into()))
|
||||
Some(Type::Object(t, name.as_str(&self.source).into()))
|
||||
}
|
||||
|
||||
fn type_of_field_decl(&self, tree: &Tree) -> Option<Type> {
|
||||
|
|
@ -2013,7 +2089,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
// Form 2: { x, ... }
|
||||
let environment = self.environment_of(t);
|
||||
let id = tree.nth_token(0)?;
|
||||
let id = tree.nth_token(0)?.as_str(&self.source);
|
||||
let declaration = match environment.bind(id) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
|
|
@ -2038,6 +2114,13 @@ impl<'a> Semantics<'a> {
|
|||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
Declaration::Import { .. } => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{id}' is an imported module, and cannot be the value of a field"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2063,12 +2146,12 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
fn type_of_match_expression(&self, tree: &Tree) -> Option<Type> {
|
||||
Some(self.type_of(tree.child_of_kind(self.syntax_tree, TreeKind::MatchBody)?))
|
||||
Some(self.type_of(tree.child_of_kind(&self.syntax_tree, TreeKind::MatchBody)?))
|
||||
}
|
||||
|
||||
fn type_of_match_body(&self, tree: &Tree) -> Option<Type> {
|
||||
let arms: Vec<_> = tree
|
||||
.children_of_kind(self.syntax_tree, TreeKind::MatchArm)
|
||||
.children_of_kind(&self.syntax_tree, TreeKind::MatchArm)
|
||||
.collect();
|
||||
|
||||
if arms.len() == 0 {
|
||||
|
|
@ -2106,7 +2189,15 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
fn type_of_pattern(&self, tree: &Tree) -> Option<Type> {
|
||||
// We know that we have a type expression in here, that's what we're asking about.
|
||||
Some(self.type_of(tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?))
|
||||
Some(self.type_of(tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)?))
|
||||
}
|
||||
|
||||
fn type_of_import(&self, tree: &Tree) -> Option<Type> {
|
||||
let tok = tree.nth_token(1)?;
|
||||
if tok.kind != TokenKind::String {
|
||||
return Some(Type::Error); // Already reported as syntax error
|
||||
}
|
||||
Some(Type::Module(tok.as_str(&self.source).into()))
|
||||
}
|
||||
|
||||
// TODO: Really want to TEST THIS also uh can we generate bytecode for functions and call it??
|
||||
|
|
@ -2118,10 +2209,10 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::LiteralExpression => {
|
||||
let tok = tree.nth_token(0)?;
|
||||
match self.type_of(t) {
|
||||
Type::F64 => Some(StackValue::Float(tok.as_str().parse().unwrap())),
|
||||
Type::F64 => Some(StackValue::Float(tok.as_str(&self.source).parse().unwrap())),
|
||||
Type::Bool => Some(StackValue::Bool(tok.kind == TokenKind::True)),
|
||||
Type::String => Some(StackValue::String(
|
||||
string_constant_to_string(tok.as_str()).into(),
|
||||
string_constant_to_string(tok.as_str(&self.source)).into(),
|
||||
)),
|
||||
Type::Nothing => Some(StackValue::Nothing), // ?
|
||||
_ => None,
|
||||
|
|
@ -2132,7 +2223,7 @@ impl<'a> Semantics<'a> {
|
|||
let pt = tree.nth_tree(2)?;
|
||||
let pattern = &self.syntax_tree[pt];
|
||||
if pattern
|
||||
.child_of_kind(self.syntax_tree, TreeKind::WildcardPattern)
|
||||
.child_of_kind(&self.syntax_tree, TreeKind::WildcardPattern)
|
||||
.is_some()
|
||||
{
|
||||
Some(StackValue::Bool(true))
|
||||
|
|
@ -2240,7 +2331,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
|
||||
eprintln!("Parsed the tree as:");
|
||||
eprintln!("\n{}", self.syntax_tree.dump(true));
|
||||
eprintln!("\n{}", self.syntax_tree.dump(&self.source, true));
|
||||
|
||||
{
|
||||
let errors = self.snapshot_errors();
|
||||
|
|
@ -2288,6 +2379,9 @@ impl<'a> Semantics<'a> {
|
|||
Declaration::Class { declaration, .. } => {
|
||||
eprintln!(" (class {declaration:?})");
|
||||
}
|
||||
Declaration::Import { declaration, .. } => {
|
||||
eprintln!(" (imported module {declaration:?})");
|
||||
}
|
||||
};
|
||||
}
|
||||
environment = env.parent.clone();
|
||||
|
|
@ -2376,6 +2470,10 @@ pub fn check(s: &Semantics) {
|
|||
TreeKind::MatchExpression => {}
|
||||
|
||||
TreeKind::WhileStatement => check_while_statement(s, tree),
|
||||
|
||||
TreeKind::Import => {
|
||||
// TODO: Check Import Statement
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2399,12 +2497,12 @@ fn check_function_decl(s: &Semantics, t: TreeRef, tree: &Tree) {
|
|||
assert_eq!(tree.kind, TreeKind::FunctionDecl);
|
||||
let _ = s.environment_of(t);
|
||||
|
||||
let return_type_tree = tree.child_of_kind(s.syntax_tree, TreeKind::ReturnType);
|
||||
let return_type_tree = tree.child_of_kind(&s.syntax_tree, TreeKind::ReturnType);
|
||||
let return_type = return_type_tree
|
||||
.map(|t| s.type_of(t))
|
||||
.unwrap_or(Type::Nothing);
|
||||
|
||||
if let Some(body) = tree.child_of_kind(s.syntax_tree, TreeKind::Block) {
|
||||
if let Some(body) = tree.child_of_kind(&s.syntax_tree, TreeKind::Block) {
|
||||
let body_type = s.type_of(body);
|
||||
if !s.can_convert(&body_type, &return_type) {
|
||||
// Just work very hard to get an appropriate error span.
|
||||
|
|
@ -2418,8 +2516,7 @@ fn check_function_decl(s: &Semantics, t: TreeRef, tree: &Tree) {
|
|||
let end_tok = tree
|
||||
.nth_token(1)
|
||||
.unwrap_or_else(|| tree.nth_token(0).unwrap());
|
||||
let end_pos = end_tok.start + end_tok.as_str().len();
|
||||
(start, end_pos)
|
||||
(start, end_tok.end())
|
||||
});
|
||||
|
||||
s.report_error_span(start, end, format!("the body of this function yields a value of type '{body_type}', but callers expect this function to produce a '{return_type}'"));
|
||||
|
|
@ -2434,9 +2531,11 @@ fn check_let(s: &Semantics, tree: &Tree) {
|
|||
let Some(expr) = tree.nth_tree(3) else { return };
|
||||
|
||||
if let Type::Method(..) = s.type_of(expr) {
|
||||
let start = name.start;
|
||||
let end = name.start + name.as_str().len();
|
||||
s.report_error_span(start, end, "methods cannot be assigned to variables");
|
||||
s.report_error_span(
|
||||
name.start(),
|
||||
name.end(),
|
||||
"methods cannot be assigned to variables",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2491,7 +2590,7 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
|
|||
let Some(type_expression) = tree.nth_tree(1) else {
|
||||
return;
|
||||
};
|
||||
let Some(field_list) = tree.child_tree_of_kind(s.syntax_tree, TreeKind::FieldList) else {
|
||||
let Some(field_list) = tree.child_tree_of_kind(&s.syntax_tree, TreeKind::FieldList) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
@ -2502,11 +2601,11 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
|
|||
|
||||
let mut any_errors = false;
|
||||
let mut field_bindings = HashMap::new();
|
||||
for field in field_list.children_of_kind(s.syntax_tree, TreeKind::FieldValue) {
|
||||
for field in field_list.children_of_kind(&s.syntax_tree, TreeKind::FieldValue) {
|
||||
let f = &s.syntax_tree[field];
|
||||
if let Some(name) = f.nth_token(0) {
|
||||
let field_type = s.type_of(field);
|
||||
field_bindings.insert(name.as_str(), (field, field_type));
|
||||
field_bindings.insert(name.as_str(&s.source), (field, field_type));
|
||||
} else {
|
||||
any_errors = true;
|
||||
}
|
||||
|
|
@ -2552,12 +2651,13 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
|
|||
|
||||
fn check_class_declaration(s: &Semantics, tree: &Tree) {
|
||||
let mut fields = HashMap::new();
|
||||
for field in tree.children_of_kind(s.syntax_tree, TreeKind::FieldDecl) {
|
||||
for field in tree.children_of_kind(&s.syntax_tree, TreeKind::FieldDecl) {
|
||||
let f = &s.syntax_tree[field];
|
||||
let Some(name) = f.nth_token(0) else {
|
||||
continue;
|
||||
};
|
||||
match fields.insert(name.as_str(), field) {
|
||||
let name = name.as_str(&s.source);
|
||||
match fields.insert(name, field) {
|
||||
Some(_) => {
|
||||
s.report_error_tree(f, format!("duplicate definition of field '{name}'"));
|
||||
}
|
||||
|
|
@ -2595,7 +2695,7 @@ fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {
|
|||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_pattern_analysis/usefulness/index.html
|
||||
|
||||
// let arms: Vec<_> = tree
|
||||
// .children_of_kind(s.syntax_tree, TreeKind::MatchArm)
|
||||
// .children_of_kind(&s.syntax_tree, TreeKind::MatchArm)
|
||||
// .collect();
|
||||
|
||||
// if arms.len() > 0 {
|
||||
|
|
@ -2626,8 +2726,9 @@ mod tests {
|
|||
#[test]
|
||||
#[should_panic(expected = "INTERNAL COMPILER ERROR: oh no")]
|
||||
pub fn ice() {
|
||||
let (tree, lines) = parse("1 + 1");
|
||||
let semantics = Semantics::new(&tree, &lines);
|
||||
let source: Rc<str> = "1+1".into();
|
||||
let (tree, lines) = parse(&source);
|
||||
let semantics = Semantics::new(source, tree.clone(), lines);
|
||||
semantics.internal_compiler_error(tree.root(), "oh no");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue