oden/fine/src/semantics.rs
John Doty 0b0b5d72d0 [fine] Tokens are by reference, ephemera
It's another jump, perhaps, but smaller arrays, and now we can track
ephemera efficiently without bloating child trees. (We could also
put ephemera inline with the child trees but then nth_token would be
unwieldy, and it would lower our data density.)
2024-04-06 17:39:33 -07:00

3212 lines
114 KiB
Rust

use crate::{
parser::{Child, SyntaxTree, TokenRef, Tree, TreeKind, TreeRef},
tokens::{Lines, Token, TokenKind},
vm::StackValue,
};
use std::{
cell::{OnceCell, RefCell},
collections::HashMap,
fmt,
rc::{Rc, Weak},
};
// TODO: Unused variables?
// TODO: Underscore for discard?
// TODO: An error should have:
//
// - a start
// - an end
// - a focus
// - descriptive messages
//
// that will have to wait for now
#[derive(Clone, PartialEq, Eq)]
pub struct Error {
pub file: Rc<str>,
pub start: (usize, usize),
pub end: (usize, usize),
pub span: (usize, usize),
pub message: String,
}
impl Error {
pub fn new<T>(
file: Rc<str>,
start: (usize, usize),
end: (usize, usize),
span: (usize, usize),
message: T,
) -> Self
where
T: ToString,
{
Error {
file,
start,
end,
span,
message: message.to_string(),
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}:{}:{}: {}",
self.file, self.start.0, self.start.1, self.message
)
}
}
pub struct FieldDecl {
pub name: Rc<str>,
pub field_type: Type,
pub declaration: TreeRef,
}
pub struct MethodDecl {
pub name: Rc<str>,
pub decl_type: Type,
pub declaration: TreeRef,
pub is_static: bool,
}
pub struct ClassDecl {
pub name: Rc<str>,
pub fields: Vec<FieldDecl>,
pub methods: Vec<MethodDecl>,
pub decl_tree: TreeRef,
pub env: EnvironmentRef,
pub static_env: EnvironmentRef,
}
#[derive(Clone)]
pub struct ClassRef(Rc<ClassDecl>);
impl ClassRef {
pub fn new(class: ClassDecl) -> Self {
ClassRef(Rc::new(class))
}
}
impl std::ops::Deref for ClassRef {
type Target = ClassDecl;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ModuleId(u64);
impl From<u64> for ModuleId {
fn from(value: u64) -> Self {
ModuleId(value)
}
}
impl fmt::Display for ModuleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
pub struct ModuleTable {
modules: HashMap<ModuleId, Weak<Semantics>>,
}
impl ModuleTable {
pub fn new() -> ModuleTable {
ModuleTable {
modules: HashMap::new(),
}
}
pub fn set_module(&mut self, id: ModuleId, semantics: Weak<Semantics>) {
self.modules.insert(id, semantics);
}
pub fn get_module(&self, id: &ModuleId) -> Option<Rc<Semantics>> {
self.modules.get(id).map(|s| s.upgrade()).flatten()
}
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, ModuleId, Weak<Semantics>> {
self.modules.iter()
}
}
#[derive(Clone)]
pub enum Type {
// Signals a type error. If you receive this then you know that an error
// has already been reported; if you produce this be sure to also note
// the error in the errors collection.
Error(Rc<Error>),
// Signals that the expression has a control-flow side-effect and that no
// value will ever result from this expression. Usually this means
// everything's fine.
Unreachable,
// The type of an assignment expression. Assignments look like
// expressions but cannot be used in places that expect them (e.g., in
// `if` conditions), and so this is how we signal that. (We can, however,
// chain assignments, and so we flow the type of the assignment through.)
Assignment(Box<Type>),
// An potentially-bound type variable.
// We need to ... like ... unify these things if possible.
TypeVariable(TreeRef),
Nothing,
// TODO: Numeric literals should be implicitly convertable, unlike other
// types. Maybe just "numeric literal" type?
F64,
I64,
String,
Bool,
Function(Vec<Box<Type>>, Box<Type>),
// A method is like a function except that it takes a self parameter.
// This is how we signal the difference. We do *not* count them as the
// same.
Method(Box<Type>, Vec<Box<Type>>, Box<Type>),
List(Box<Type>),
// A class is the static type of a class; when the class is referred to
// by name it has this type. (Distinct from an instance!)
Class(ModuleId, TreeRef, Rc<str>),
// An object is the type of an allocated object instance. Details of its
// class need to be fetched explicitly from the semantics via the
// TreeRef and `Semantics::class_of`; they are computed lazily.
Object(ModuleId, TreeRef, Rc<str>),
// An alternate is one or another type.
Alternate(Box<[Type]>),
// A module of some kind. What module?
Module(Rc<str>, ModuleId),
}
impl Type {
pub fn is_error(&self) -> bool {
match self {
Type::Error(..) => true,
_ => false,
}
}
fn discriminant_number(&self) -> i8 {
match self {
Type::Error(..) => 0,
Type::Unreachable => 1,
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::Module(..) => 15,
}
}
}
impl fmt::Debug for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Type::*;
match self {
Error(e) => write!(f, "<< INTERNAL ERROR ({e}) >>"),
Unreachable => write!(f, "<< UNREACHABLE >>"),
Assignment(_) => write!(f, "assignment"),
Nothing => write!(f, "nothing"),
F64 => write!(f, "f64"),
I64 => write!(f, "i64"),
String => write!(f, "string"),
Bool => write!(f, "bool"),
Function(args, ret) => {
write!(f, "fun (")?;
let mut first = true;
for arg in args.iter() {
if !first {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
first = false;
}
write!(f, ") -> {ret}")
}
Method(self_type, args, ret) => {
write!(f, "method of {self_type} (")?;
let mut first = true;
for arg in args.iter() {
if !first {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
first = false;
}
write!(f, ") -> {ret}")
}
TypeVariable(_) => {
// TODO: Better names for type variable
write!(f, "$_")
}
List(t) => write!(f, "list<{t}>"),
Object(_, _, name) => write!(f, "{} instance", name),
Class(_, _, name) => write!(f, "class {}", name),
Alternate(ts) => {
let mut first = true;
for t in ts.iter() {
if !first {
write!(f, " or ")?;
}
write!(f, "{t}")?;
first = false;
}
Ok(())
}
Module(name, _) => write!(f, "module {}", name),
}
}
}
impl std::cmp::PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl std::cmp::Eq for Type {}
impl std::cmp::PartialOrd for Type {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for Type {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
let li = self.discriminant_number();
let ri = other.discriminant_number();
if li < ri {
Ordering::Less
} else if li > ri {
Ordering::Greater
} else {
match (self, other) {
(Type::Assignment(x), Type::Assignment(y)) => x.cmp(y),
// NOTE: This is wrong! Type variables *cannot* be compared
// like this without binding information.
(Type::TypeVariable(x), Type::TypeVariable(y)) => x.index().cmp(&y.index()),
(Type::Function(la, lr), Type::Function(ra, rr)) => {
let lv = (la, lr);
let rv = (ra, rr);
lv.cmp(&rv)
}
(Type::Method(lo, la, lr), Type::Method(ro, ra, rr)) => {
let lv = (lo, la, lr);
let rv = (ro, ra, rr);
lv.cmp(&rv)
}
(Type::List(x), Type::List(y)) => x.cmp(y),
(Type::Class(lm, lt, _), Type::Class(rm, rt, _)) => {
(lm.0, lt.index()).cmp(&(rm.0, rt.index()))
}
(Type::Object(lm, lt, _), Type::Object(rm, rt, _)) => {
(lm.0, lt.index()).cmp(&(rm.0, rt.index()))
}
(Type::Alternate(ll), Type::Alternate(rr)) => ll.cmp(rr),
_ => Ordering::Equal,
}
}
}
}
// NOTE: I tried to actually embed the coordinate inside the location but
// that doesn't work well for other things we want to express, so we
// leave it alone. A data modeling maximalist might make *two* enums
// (with and without a coordinate) but... that's a lot of complexity
// for very little gain. Maybe we can come back to it when things this
// design is a little more stable.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Location {
Argument, // An argument to a function
Local, // A local in an frame
Slot, // A slot in an object
Module, // A global in a module
Function, // A function in a module
ExternalFunction, // An external function (module unrelated)
Class, // A class in a module
Import, // An import in a module (index unrelated)
}
// TODO: Is `usize` what we want? Do we want e.g. dyn trait for invoke?
#[derive(Clone, Debug)]
pub struct ExternalFunctionId(usize);
impl ExternalFunctionId {
pub fn id(&self) -> usize {
self.0
}
}
#[derive(Clone, Debug)]
pub enum Origin {
Source(TreeRef),
External(Type),
}
#[derive(Clone, Debug)]
pub struct Declaration {
pub location: Location,
pub index: usize,
pub module: ModuleId,
pub origin: Origin,
pub exported: bool,
}
impl Declaration {
pub fn is_exported(&self) -> bool {
self.exported
}
pub fn tree(&self) -> Option<TreeRef> {
match self.origin {
Origin::Source(t) => Some(t),
_ => None,
}
}
pub fn set_exported(&mut self) {
self.exported = true
}
}
pub struct Environment {
pub parent: Option<EnvironmentRef>,
pub module: ModuleId,
pub location: Location,
pub next_index: usize,
pub declarations: HashMap<Box<str>, Declaration>,
pub error: Option<Rc<Error>>,
}
impl Environment {
pub fn new(module: ModuleId, parent: Option<EnvironmentRef>, location: Location) -> Self {
let parent_location = parent
.as_ref()
.map(|p| p.location)
.unwrap_or(Location::Module);
let base = parent.as_ref().map(|p| p.next_index).unwrap_or(0);
let next_index = match (parent_location, location) {
(_, Location::Argument) => 0,
(_, Location::Slot) => 0,
(Location::Local, Location::Local) => base,
(_, Location::Local) => 0,
(Location::Module, Location::Module) => base,
_ => panic!("{location:?} is not suitable as a default location"),
};
Environment {
parent,
module,
location,
next_index,
declarations: HashMap::new(),
error: None,
}
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn error(why: Rc<Error>) -> EnvironmentRef {
EnvironmentRef::new(Environment {
parent: None,
module: ModuleId(0),
location: Location::Local,
next_index: 0,
declarations: HashMap::new(),
error: Some(why),
})
}
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> {
let result = self.declarations.insert(
name,
Declaration {
location: self.location,
index: self.next_index,
module: self.module,
origin: Origin::Source(t),
exported: false,
},
);
self.next_index += 1;
result
}
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) {
return Some(decl);
}
current = &env.parent;
}
None
}
}
#[derive(Clone)]
pub struct EnvironmentRef(Rc<Environment>);
impl EnvironmentRef {
pub fn new(environment: Environment) -> Self {
EnvironmentRef(Rc::new(environment))
}
}
impl std::ops::Deref for EnvironmentRef {
type Target = Environment;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn set_logical_parents(
parents: &mut Vec<Option<TreeRef>>,
syntax_tree: &SyntaxTree,
t: TreeRef,
parent: Option<TreeRef>,
) {
parents[t.index()] = parent.clone();
let tree = &syntax_tree[t];
// eprintln!("SET PARENT {parent:?} => CHILD {tree:?} ({t:?})");
match tree.kind {
TreeKind::Block | TreeKind::File => {
// In a block (or at the top level), each child actually points
// to the previous child as the logical parent, so that variable
// declarations that occur as part of statements in the block are
// available to statements later in the block.
let mut parent = Some(t);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
set_logical_parents(parents, syntax_tree, *ct, parent);
parent = Some(*ct);
}
}
}
}
TreeKind::LetStatement => {
// In a let statement, the logical parent of the children is
// actually the logical parent of the let statement, so that the
// variable doesn't have itself in scope. :P
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => set_logical_parents(parents, syntax_tree, *ct, parent),
}
}
}
TreeKind::FunctionDecl => {
// In a function declaration, the logical parent of the body is
// the parameter list.
let param_list = tree.child_of_kind(syntax_tree, TreeKind::ParamList);
let body = tree.child_of_kind(syntax_tree, TreeKind::Block);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
if Some(*ct) == body {
set_logical_parents(parents, syntax_tree, *ct, param_list);
} else {
set_logical_parents(parents, syntax_tree, *ct, Some(t));
}
}
}
}
}
TreeKind::ForStatement => {
let body = tree.child_of_kind(syntax_tree, TreeKind::Block);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
if Some(*ct) == body {
set_logical_parents(parents, syntax_tree, *ct, Some(t));
} else {
// If it's not the body then it must be the
// iterable and the iterable doesn't have the
// loop variable in scope.
set_logical_parents(parents, syntax_tree, *ct, parent);
}
}
}
}
}
TreeKind::ConditionalExpression => {
// Special case! The parent of the `then` clause is the
// condition, so any variable bound by the condition is valid in
// the `then` clause. The `else` clause and the condition itself
// do not have the bindings in scope, obviously.
let body_parent = if let Some(is_condition) = tree.nth_tree(1) {
Some(is_condition)
} else {
Some(t)
};
let then_body = tree.nth_tree(2);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
if Some(*ct) == then_body {
set_logical_parents(parents, syntax_tree, *ct, body_parent);
} else {
set_logical_parents(parents, syntax_tree, *ct, Some(t));
}
}
}
}
}
TreeKind::WhileStatement => {
// Just like `if`, bindings in the condition are valid in the body.
let body_parent = if let Some(is_condition) = tree.nth_tree(1) {
Some(is_condition)
} else {
Some(t)
};
let then_body = tree.nth_tree(2);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
if Some(*ct) == then_body {
set_logical_parents(parents, syntax_tree, *ct, body_parent);
} else {
set_logical_parents(parents, syntax_tree, *ct, Some(t));
}
}
}
}
}
_ => {
// By default, the parent for each child is current tree.
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => set_logical_parents(parents, syntax_tree, *ct, Some(t)),
}
}
}
}
}
// Process escapes and convert a string constant in source to a runtime String value.
pub fn string_constant_to_string(s: &str) -> String {
let mut result = String::new();
if s.len() <= 2 {
return result;
}
let mut input = s[1..s.len() - 1].chars();
while let Some(ch) = input.next() {
if ch == '\\' {
if let Some(ch) = input.next() {
match ch {
'n' => result.push('\n'),
'r' => result.push('\r'),
't' => result.push('\t'),
_ => result.push(ch),
}
} else {
result.push(ch)
}
} else {
result.push(ch)
}
}
result
}
#[derive(Clone, Copy, Debug)]
enum Incremental<T> {
None,
InProgress,
Complete(T),
}
pub struct Semantics {
mid: ModuleId,
file: Rc<str>,
source: Rc<str>,
syntax_tree: Rc<SyntaxTree>,
lines: Rc<Lines>,
module_table: RefCell<Rc<ModuleTable>>,
import_map: OnceCell<HashMap<String, ModuleId>>,
// Instead of physical parents, this is the set of *logical* parents.
// This is what is used for binding.
logical_parents: Vec<Option<TreeRef>>,
function_count: usize,
function_indices: Vec<Option<usize>>,
// TODO: State should be externalized instead of this refcell nonsense.
errors: RefCell<Vec<Rc<Error>>>,
types: RefCell<Vec<Incremental<Type>>>,
environments: RefCell<Vec<Incremental<EnvironmentRef>>>,
root_environment: EnvironmentRef,
classes: RefCell<Vec<Incremental<ClassRef>>>,
}
impl std::ops::Index<TreeRef> for Semantics {
type Output = Tree;
#[inline]
fn index(&self, index: TreeRef) -> &Self::Output {
&self.syntax_tree[index]
}
}
impl std::ops::Index<&TreeRef> for Semantics {
type Output = Tree;
#[inline]
fn index(&self, index: &TreeRef) -> &Self::Output {
&self.syntax_tree[index]
}
}
impl std::ops::Index<TokenRef> for Semantics {
type Output = Token;
#[inline]
fn index(&self, index: TokenRef) -> &Self::Output {
&self.syntax_tree[index]
}
}
impl std::ops::Index<&TokenRef> for Semantics {
type Output = Token;
#[inline]
fn index(&self, index: &TokenRef) -> &Self::Output {
&self.syntax_tree[index]
}
}
impl Semantics {
pub fn new(
mid: ModuleId,
file: Rc<str>,
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);
}
let root_environment = Environment::new(mid, None, Location::Module);
let mut function_count = 0;
let mut function_indices = vec![None; tree.len()];
for t in tree.trees() {
let tree = &tree[t];
match tree.kind {
TreeKind::FunctionDecl | TreeKind::ClassDecl => {
function_indices[t.index()] = Some(function_count);
function_count += 1;
}
_ => {}
}
}
let mut semantics = Semantics {
mid,
file,
source,
syntax_tree: tree.clone(),
lines,
module_table: RefCell::new(Rc::new(ModuleTable::new())),
import_map: OnceCell::new(),
logical_parents,
function_count,
function_indices,
errors: RefCell::new(vec![]),
types: RefCell::new(vec![Incremental::None; tree.len()]),
environments: RefCell::new(vec![Incremental::None; tree.len()]),
root_environment: EnvironmentRef::new(root_environment),
classes: RefCell::new(vec![Incremental::None; tree.len()]),
};
// NOTE: We ensure all the known errors are reported before we move
// on to answering any other questions. We're going to work as
// hard as we can from a partial tree.
if let Some(tr) = semantics.syntax_tree.root() {
semantics.gather_errors(tr);
}
semantics
}
pub fn set_imports(&self, imports: HashMap<String, ModuleId>) {
self.import_map.set(imports).expect("imports already set");
}
pub fn set_module_table(&self, table: Rc<ModuleTable>) {
self.module_table.replace(table);
}
pub fn import_ids(&self) -> Vec<ModuleId> {
let import_map = self.import_map.get().unwrap();
import_map.values().map(|id| *id).collect()
}
pub fn import_by_id(&self, mid: ModuleId) -> Option<Rc<Semantics>> {
self.module_table.borrow().get_module(&mid)
}
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 imports(&self) -> Vec<String> {
self.syntax_tree
.root()
.map(|file| {
self[file]
.children_of_kind(&self.syntax_tree, TreeKind::Import)
.filter_map(|import| {
let tok = &self[self[import].nth_token(1)?];
if tok.kind != TokenKind::String {
None
} else {
Some(string_constant_to_string(tok.as_str(&self.source)))
}
})
.collect()
})
.unwrap_or(Vec::new())
}
pub fn snapshot_errors(&self) -> Vec<Rc<Error>> {
let mut result = (*self.errors.borrow()).clone();
result.sort_by_key(|a| a.span.0);
result
}
pub fn logical_parent(&self, tr: TreeRef) -> Option<TreeRef> {
if tr.index() < self.logical_parents.len() {
self.logical_parents[tr.index()]
} else {
None
}
}
pub fn mid(&self) -> ModuleId {
self.mid
}
fn report_error_span<T>(&self, start_pos: usize, end_pos: usize, error: T) -> Rc<Error>
where
T: ToString,
{
let start = self.lines.position(start_pos);
let end = self.lines.position(end_pos);
let error = Rc::new(Error::new(
self.file.clone(),
start,
end,
(start_pos, end_pos),
error.to_string(),
));
self.errors.borrow_mut().push(error.clone());
error
}
fn report_error_tree<T>(&self, tree: &Tree, error: T) -> Rc<Error>
where
T: ToString,
{
self.report_error_span(tree.start_pos, tree.end_pos, error)
}
fn report_error_tree_ref<T>(&self, tree: TreeRef, error: T) -> Rc<Error>
where
T: ToString,
{
let tree = &self[tree];
self.report_error_span(tree.start_pos, tree.end_pos, error)
}
fn gather_errors(&mut self, tree: TreeRef) {
let mut stack = vec![tree];
while let Some(tr) = stack.pop() {
let tree = &self[tr];
for child in &tree.children {
match child {
Child::Token(t) => {
let t = &self[*t];
if t.kind == TokenKind::Error {
self.report_error_span(t.start(), t.end(), t.as_str(&self.source));
}
}
Child::Tree(t) => stack.push(*t),
}
}
}
}
// TODO: Here we're just looking for *an* error, not the most specific
// error.
fn find_error(&self, tree: &Tree) -> Option<Rc<Error>> {
let mut result = (*self.errors.borrow()).clone();
result.sort_by_key(|a| a.span.0);
let mut error = None;
for candidate in result.into_iter() {
let (candiate_start, candidate_end) = candidate.span;
if candidate_end < tree.start_pos {
continue;
}
if candiate_start > tree.end_pos {
break;
}
// End is after our point, Start is before our point, we are
// inside. This error at least affects us somehow.
error = Some(candidate);
}
error
}
fn type_error_for(&self, tree: &Tree) -> Type {
let Some(error) = self.find_error(&tree) else {
self.internal_compiler_error(
Some(tree.self_ref),
"Unable to find a diagnostic that encompasses the tree generating an error type",
);
};
Type::Error(error)
}
fn environment_error_for(&self, tree: &Tree) -> EnvironmentRef {
let Some(error) = self.find_error(&tree) else {
self.internal_compiler_error(
Some(tree.self_ref),
"Unable to find a diagnostic that encompasses the tree generating an error environment",
);
};
Environment::error(error)
}
pub fn function_count(&self) -> usize {
self.function_count
}
pub fn get_function_index(&self, t: TreeRef) -> Option<usize> {
let index = t.index();
if index >= self.function_indices.len() {
None
} else {
self.function_indices[t.index()]
}
}
pub fn function_index_of(&self, t: TreeRef) -> usize {
let Some(index) = self.function_indices[t.index()] else {
self.internal_compiler_error(Some(t), "Why didn't I get a function index for this?");
};
index
}
pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef {
{
// I want to make sure that this borrow is dropped after this block.
let mut borrow = self.environments.borrow_mut();
let state = &mut borrow[t.index()];
match state {
Incremental::None => (),
Incremental::Complete(e) => return e.clone(),
Incremental::InProgress => {
// NOTE: Set the state so the ICE doesn't loop on itself.
*state = Incremental::Complete(self.root_environment.clone());
drop(borrow);
//eprintln!("environment_of circular => {t:?}");
self.internal_compiler_error(Some(t), "circular environment dependency");
}
}
*state = Incremental::InProgress;
}
let tree = &self[t];
// eprintln!(">>> environment_of => {tree:?}");
let parent = match self.logical_parents[t.index()] {
Some(t) => self.environment_of(t),
None => self.root_environment.clone(),
};
let result = match tree.kind {
TreeKind::Block => self.environment_of_block(parent, tree),
TreeKind::File => self.environment_of_file(parent, tree),
TreeKind::ForStatement => self.environment_of_for(parent, tree),
TreeKind::IsExpression => self.environment_of_is_expression(parent, tree),
TreeKind::LetStatement => self.environment_of_let(parent, tree),
TreeKind::MatchArm => self.environment_of_match_arm(parent, t, tree),
TreeKind::ParamList => self.environment_of_paramlist(parent, tree),
_ => parent,
};
self.environments.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
// eprintln!("<<< environment_of => {tree:?}");
result
}
fn environment_of_block(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
let mut environment = Environment::new(self.mid, Some(parent), Location::Local);
for child in tree.children.iter() {
match child {
Child::Tree(t) => {
let ct = &self[*t];
if ct.kind == TreeKind::FunctionDecl {
let Some(name) = ct.nth_token(1) else {
continue;
};
let existing = environment.declarations.insert(
self[name].as_str(&self.source).into(),
Declaration {
location: Location::Function,
index: self.function_index_of(*t),
module: self.mid,
origin: Origin::Source(*t),
exported: false,
},
);
if existing.is_some() {
self.report_error_tree(
ct,
format!(
"duplicate definition of function '{}'",
self[name].as_str(&self.source)
),
);
}
}
}
_ => {}
}
}
EnvironmentRef::new(environment)
}
fn environment_of_file(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
let mut environment = Environment::new(self.mid, Some(parent), Location::Module);
let mut explicit_exports = Vec::new();
for child in tree.children.iter() {
let Child::Tree(t) = child else {
continue;
};
let binding = {
// Redeclare t to be mutable (and a copy)
let mut t = *t;
let mut exported = false;
// Loop here in order to dereference TreeKind::Export;
// children of an export tree still go in the local
// environment.
loop {
let ct = &self[t];
match ct.kind {
TreeKind::FunctionDecl => {
let Some(name) = ct.nth_token(1) else {
break None;
};
let name = &self[name];
if name.kind != TokenKind::Identifier {
break None;
}
let declaration = Declaration {
location: Location::Function,
index: self.function_index_of(t),
module: self.mid,
origin: Origin::Source(t),
exported,
};
break Some(("function", name, declaration));
}
TreeKind::ClassDecl => {
let Some(name) = ct.nth_token(1) else {
break None;
};
let name = &self[name];
if name.kind != TokenKind::Identifier {
break None;
}
let declaration = Declaration {
location: Location::Class,
index: self.function_index_of(t),
module: self.mid,
origin: Origin::Source(t),
exported,
};
break Some(("class", name, declaration));
}
TreeKind::Import => {
let Some(name) = ct.nth_token(3) else {
break None;
};
let name = &self[name];
if name.kind != TokenKind::Identifier {
break None;
}
let declaration = Declaration {
location: Location::Import,
index: 0,
module: self.mid,
origin: Origin::Source(t),
exported,
};
break Some(("import", name, declaration));
}
TreeKind::Export => {
let Some(inner) = ct.nth_tree(1) else {
break None;
};
t = inner;
exported = true;
continue;
}
TreeKind::ExportList => {
for child in &ct.children {
if let Child::Token(tok) = child {
let tok = &self[tok];
if tok.kind == TokenKind::Identifier {
explicit_exports.push(tok);
}
}
}
}
_ => break None,
}
}
};
let ct = &self[*t];
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)
),
);
}
}
}
for tok in explicit_exports {
environment
.declarations
.get_mut(tok.as_str(&self.source))
// NOTE: If not present, we report the error elsewhere.
.map(|decl| decl.set_exported());
}
EnvironmentRef::new(environment)
}
fn environment_of_let(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
let Some(name) = tree.nth_token(1) else {
return parent; // Error is already reported, don't clobber parent bindings.
};
let Some(declaration) = tree.nth_tree(3) else {
return parent; // Error is already reported, don't clobber parent bindings.
};
let location = match parent.location {
Location::Local => Location::Local,
Location::Module => Location::Module,
Location::Argument => Location::Local,
Location::Slot => Location::Local,
_ => {
let message = format!(
"Unsuitable environment location for a let: {:?}",
parent.location
);
self.internal_compiler_error(Some(tree.self_ref), &message);
}
};
let mut environment = Environment::new(self.mid, Some(parent), location);
environment.insert(self[name].as_str(&self.source), declaration);
EnvironmentRef::new(environment)
}
fn environment_of_paramlist(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
assert!(tree.kind == TreeKind::ParamList);
let mut environment = Environment::new(self.mid, Some(parent), Location::Argument);
for (i, child) in tree.children.iter().enumerate() {
let Child::Tree(ct) = child else {
continue;
};
let param = &self[*ct];
match param.kind {
TreeKind::SelfParameter => {
let param_name = &self[param.nth_token(0).unwrap()];
if environment
.insert(param_name.as_str(&self.source), *ct)
.is_some()
{
self.report_error_tree(
param,
format!("duplicate definition of self parameter"),
);
} else if i != 1 {
self.report_error_tree(
param,
"self parameter must be the first parameter} in the list",
);
}
}
TreeKind::Parameter => {
let Some(param_name) = param.nth_token(0) else {
continue;
};
let param_name = &self[param_name];
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_str}'"),
);
}
}
_ => (),
}
}
EnvironmentRef::new(environment)
}
fn environment_of_for(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
let Some(it) = tree.nth_tree(1) else {
return parent;
};
let iterator = &self[it];
let Some(id) = iterator.nth_token(0) else {
return parent;
};
let mut environment = Environment::new(self.mid, Some(parent), Location::Local);
environment.insert(&self[id].as_str(&self.source), it);
EnvironmentRef::new(environment)
}
fn environment_of_is_expression(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
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 {
// Should really have a pattern in there; otherwise there was a
// parse error, don't make more trouble.
return self.environment_error_for(tree);
};
// The left hand side of the `is` expression is used for wildcard types.
let Some(lhs) = tree.nth_tree(0) else {
return self.environment_error_for(tree);
};
self.environment_of_pattern(parent, pattern, lhs)
}
fn environment_of_match_arm(
&self,
parent: EnvironmentRef,
t: TreeRef,
tree: &Tree,
) -> EnvironmentRef {
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 {
// Should really have a pattern in there; otherwise there was a
// parse error, don't make more trouble.
return self.environment_error_for(tree);
};
// The expression in the match expression is the binding for the wildcard pattern.
// If we are somewhere weird then... uh....
let Some(match_body) = tree.parent else {
self.internal_compiler_error(Some(t), "no parent on match arm");
};
let match_body = &self[match_body];
if match_body.kind != TreeKind::MatchBody {
self.internal_compiler_error(Some(t), "match arm parent not match body");
}
let Some(match_expression) = match_body.parent else {
self.internal_compiler_error(Some(t), "no parent on match body");
};
let match_expression = &self[match_expression];
if match_expression.kind != TreeKind::MatchExpression {
self.internal_compiler_error(Some(t), "match body parent not match expression");
}
// The expression is the first tree child of match expression.
let Some(lhs) = tree.nth_tree(2) else {
return self.environment_error_for(tree);
};
self.environment_of_pattern(parent, pattern, lhs)
}
// NOTE: THIS IS CALLED DIRECTLY, NOT VIA `environment_of` TO AVOID CYCLES.
fn environment_of_pattern(
&self,
parent: EnvironmentRef,
tree: &Tree,
value_expr: TreeRef,
) -> EnvironmentRef {
assert_eq!(tree.kind, TreeKind::Pattern);
let Some(binding) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::VariableBinding)
else {
// No binding, no new environment.
return parent;
};
let Some(variable) = binding.nth_token(0) else {
return self.environment_error_for(binding);
};
let is_wildcard = tree
.child_of_kind(&self.syntax_tree, TreeKind::WildcardPattern)
.is_some();
let variable_decl = if is_wildcard {
// If the variable is bound to a wildcard then treat the value
// expression as the declaration for the purpose of determining
// type.
value_expr
} 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)
else {
return self.environment_error_for(tree);
};
type_expr
};
// TODO: This binding should be un-assignable! Don't assign to this!
let mut env = Environment::new(self.mid, Some(parent), Location::Local);
env.insert(&self[variable].as_str(&self.source), variable_decl);
EnvironmentRef::new(env)
}
pub fn class_of(&self, mid: ModuleId, t: TreeRef) -> ClassRef {
if mid != self.mid {
let Some(other_semantics) = self.import_by_id(mid) else {
self.internal_compiler_error(Some(t), "Have a class we can't resolve");
};
return other_semantics.class_of(mid, t);
}
{
// I want to make sure that this borrow is dropped after this block.
let mut borrow = self.classes.borrow_mut();
let state = &mut borrow[t.index()];
match state {
Incremental::None => (),
Incremental::Complete(e) => return e.clone(),
Incremental::InProgress => {
drop(borrow);
self.internal_compiler_error(Some(t), "circular class dependency");
}
}
*state = Incremental::InProgress;
}
// TODO: Right now there's only one way to make a class decl. :P
let tree = &self[t];
assert_eq!(tree.kind, TreeKind::ClassDecl);
let name = tree
.nth_token(1)
.map(|t| self[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) {
let f = &self[field];
if let Some(field_name) = f.nth_token(0) {
let field_type = f
.nth_tree(2)
.map(|t| self.type_of(t))
.unwrap_or_else(|| self.type_error_for(f));
fields.push(FieldDecl {
name: self[field_name].as_str(&self.source).into(),
declaration: field,
field_type,
});
}
}
// Methods
let mut methods = Vec::new();
for method in tree.children_of_kind(&self.syntax_tree, TreeKind::FunctionDecl) {
let m = &self[method];
if let Some(method_name) = m.nth_token(1) {
let method_name = &self[method_name];
// TODO: Check to see if it is actually a method, or if it is a static function.
let decl_type = self.type_of(method);
match decl_type {
Type::Method(..) => {
methods.push(MethodDecl {
name: method_name.as_str(&self.source).into(),
decl_type,
declaration: method,
is_static: false,
});
}
_ => {
// TODO: Default to method or static?
methods.push(MethodDecl {
name: method_name.as_str(&self.source).into(),
decl_type,
declaration: method,
is_static: true,
});
}
}
}
}
// Build into an environment
let mut env = Environment::new(self.mid, None, Location::Slot);
let mut static_env = Environment::new(self.mid, None, Location::Slot);
for (index, field) in fields.iter().enumerate() {
env.declarations.insert(
(&*field.name).into(),
Declaration {
location: Location::Slot,
index,
module: self.mid,
origin: Origin::Source(field.declaration),
exported: false,
},
);
}
for method in methods.iter() {
let target = if method.is_static {
&mut static_env.declarations
} else {
&mut env.declarations
};
let existing = target.insert(
(&*method.name).into(),
Declaration {
location: Location::Function,
index: self.function_index_of(method.declaration),
module: self.mid,
origin: Origin::Source(method.declaration),
exported: false,
},
);
if existing.is_some() {
self.report_error_tree_ref(
method.declaration,
format!("duplicate definition of method '{}'", method.name),
);
}
}
let result = ClassRef::new(ClassDecl {
name: name.into(),
fields: fields.into(),
methods: methods.into(),
decl_tree: t,
env: EnvironmentRef::new(env),
static_env: EnvironmentRef::new(static_env),
});
self.classes.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
result
}
pub fn build_alternate(&self, left: &Type, right: &Type) -> Type {
if left.is_error() {
left.clone()
} else if right.is_error() {
right.clone()
} else if self.can_convert(left, right) {
right.clone()
} else if self.can_convert(right, left) {
left.clone()
} else {
let mut types: Vec<Type> = Vec::new();
if let Type::Alternate(ts) = left {
types.extend_from_slice(&*ts);
} else {
types.push(left.clone());
}
if let Type::Alternate(ts) = right {
types.extend_from_slice(&*ts);
} else {
types.push(right.clone());
}
types.sort();
types.dedup();
Type::Alternate(types.into())
}
}
pub fn can_convert(&self, from: &Type, to: &Type) -> bool {
// TODO: This is wrong; we because of numeric literals etc.
match (from, to) {
(Type::F64, Type::F64) => true,
(Type::String, Type::String) => true,
(Type::Bool, Type::Bool) => true,
(Type::Unreachable, Type::Unreachable) => true,
(Type::Nothing, Type::Nothing) => true,
(Type::List(from), Type::List(to)) => self.can_convert(from, to),
(Type::Function(from_args, from_ret), Type::Function(to_args, to_ret)) => {
from_args.len() == from_args.len()
&& self.can_convert(to_ret, from_ret)
&& from_args
.iter()
.zip(to_args.iter())
.all(|(from, to)| self.can_convert(from, to))
}
(Type::Object(m_from, c_from, _), Type::Object(m_to, c_to, _)) => {
// TODO: Structural comparisons. All that matters is that
// c_to has a subset of fields and methods, and the
// fields and methods are all compatible.
//
m_from == m_to && c_from == c_to
}
// Avoid introducing more errors
(Type::Error(_), _) => true,
(_, Type::Error(_)) => true,
// Can... I... convert unreachable always? Is this sound?
(Type::Unreachable, _) => true,
// Alternates convert if either side can convert.
(Type::Alternate(lts), Type::Alternate(_)) => {
for lt in lts.iter() {
if !self.can_convert(lt, to) {
return false;
}
}
true
}
(_, Type::Alternate(rts)) => {
for rt in rts.iter() {
if self.can_convert(from, rt) {
return true;
}
}
false
}
// TODO: Unification on type variables! :D
(_, _) => false,
}
}
pub fn type_of(&self, t: TreeRef) -> Type {
{
let state = &mut self.types.borrow_mut()[t.index()];
match state {
Incremental::None => (),
Incremental::Complete(existing) => return existing.clone(),
Incremental::InProgress => {
// eprintln!("type_of circular => {t:?}");
let error = self
.report_error_tree_ref(t, "The type of this expression depends on itself");
let e_type = Type::Error(error);
*state = Incremental::Complete(e_type.clone());
return e_type;
}
}
*state = Incremental::InProgress;
}
let tree = &self[t];
// eprintln!(">>> type_of => {tree:?}");
let result = match tree.kind {
TreeKind::Error => Some(self.type_error_for(tree)),
TreeKind::AlternateType => self.type_of_alternate_type(tree),
TreeKind::Argument => self.type_of_argument(tree),
TreeKind::BinaryExpression => self.type_of_binary(tree),
TreeKind::Block => self.type_of_block(tree),
TreeKind::CallExpression => self.type_of_call(tree),
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
TreeKind::FieldDecl => self.type_of_field_decl(tree),
TreeKind::FieldValue => self.type_of_field_value(t, tree),
TreeKind::ForStatement => Some(Type::Nothing),
TreeKind::FunctionDecl => self.type_of_function_decl(tree),
TreeKind::GroupingExpression => self.type_of_grouping(tree),
TreeKind::Identifier => self.type_of_identifier(t, tree),
TreeKind::IfStatement => self.type_of_if_statement(tree),
TreeKind::IsExpression => Some(Type::Bool),
TreeKind::IteratorVariable => self.type_of_iterator_variable(tree),
TreeKind::LetStatement => Some(Type::Nothing),
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
TreeKind::LiteralExpression => self.type_of_literal(tree),
TreeKind::MatchArm => self.type_of_match_arm(tree),
TreeKind::MatchBody => self.type_of_match_body(tree),
TreeKind::MatchExpression => self.type_of_match_expression(tree),
TreeKind::MemberAccess => self.type_of_member_access(tree),
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
TreeKind::Parameter => self.type_of_parameter(tree),
TreeKind::Pattern => self.type_of_pattern(tree),
TreeKind::ReturnStatement => Some(Type::Unreachable),
TreeKind::ReturnType => self.type_of_return_type(tree),
TreeKind::SelfParameter => self.type_of_self_parameter(tree),
TreeKind::SelfReference => self.type_of_self_reference(t, tree),
TreeKind::TypeExpression => self.type_of_type_expr(tree),
TreeKind::TypeIdentifier => self.type_of_type_identifier(t, tree),
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"),
};
// NOTE: These return `None` if they encounter some problem.
let result = result.unwrap_or_else(|| self.type_error_for(tree));
self.types.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
// eprintln!("<<< type_of => {tree:?}");
result
}
fn type_of_unary(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::UnaryExpression);
let op = &self[tree.nth_token(0)?];
let expr = tree.nth_tree(1)?;
let argument_type = self.type_of(expr);
match (op.kind, argument_type) {
(TokenKind::Plus, Type::F64) => Some(Type::F64),
(TokenKind::Minus, Type::F64) => Some(Type::F64),
(TokenKind::Bang, Type::Bool) => Some(Type::Bool),
// This is dumb and should be punished, probably.
(_, Type::Unreachable) => {
let err = self.report_error_span(
op.start(),
op.end(),
"cannot apply a unary operator to something that doesn't yield a value",
);
Some(Type::Error(err))
}
// Propagate existing errors without additional complaint.
(_, Type::Error(e)) => Some(Type::Error(e)),
(_, arg_type) => {
let err = self.report_error_span(
op.start(),
op.end(),
format!(
"cannot apply unary operator '{}' to value of type {}",
op.as_str(&self.source),
arg_type
),
);
Some(Type::Error(err))
}
}
}
fn type_of_binary(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::BinaryExpression);
let left_tree = tree.nth_tree(0)?;
let lhs = self.type_of(left_tree);
let op = &self[tree.nth_token(1)?];
let rhs = self.type_of(tree.nth_tree(2)?);
match (op.kind, lhs, rhs) {
(
TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash,
Type::F64,
Type::F64,
) => Some(Type::F64),
(TokenKind::Plus, Type::String, Type::String) => Some(Type::String),
(TokenKind::And | TokenKind::Or, Type::Bool, Type::Bool) => Some(Type::Bool),
(TokenKind::EqualEqual, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::EqualEqual, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::EqualEqual, Type::Bool, Type::Bool) => Some(Type::Bool),
(TokenKind::EqualEqual, Type::Nothing, Type::Nothing) => Some(Type::Bool),
(TokenKind::Less, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::LessEqual, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::Greater, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::GreaterEqual, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::Less, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::LessEqual, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::Greater, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::GreaterEqual, Type::String, Type::String) => Some(Type::Bool),
// This is dumb and should be punished, probably.
(_, _, Type::Unreachable) => {
let err = 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(err))
}
(_, Type::Unreachable, _) => {
let err = 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(err))
}
// Propagate existing errors without additional complaint.
(_, Type::Error(e), _) => Some(Type::Error(e)),
(_, _, Type::Error(e)) => Some(Type::Error(e)),
// Assignments are fun.
(TokenKind::Equal, a, b) => self.type_of_assignment(left_tree, a, b, &op),
// Missed the whole table, it must be an error.
(_, left_type, right_type) => {
let err =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(err))
}
}
}
fn type_of_assignment(
&self,
left_tree: TreeRef,
left_type: Type,
right_type: Type,
op: &Token,
) -> Option<Type> {
// Ensure the left tree is an lvalue
let tree = &self[left_tree];
#[allow(unused_assignments)]
let mut environment = None;
let declaration = match tree.kind {
// TODO: Assign to list access
TreeKind::Identifier => {
let id = self[tree.nth_token(0)?].as_str(&self.source);
environment = Some(self.environment_of(left_tree));
match environment.as_ref().unwrap().bind(id) {
Some(decl) => decl,
None => {
let error = if let Some(e) = &environment.as_ref().unwrap().error {
e.clone()
} else {
self.report_error_tree(tree, format!("cannot find value {id} here"))
};
return Some(Type::Error(error));
}
}
}
TreeKind::MemberAccess => {
let id = self[tree.nth_token(2)?].as_str(&self.source);
let typ = self.type_of(tree.nth_tree(0)?);
environment = Some(self.member_environment(left_tree, &typ));
match environment.as_ref().unwrap().bind(id) {
Some(decl) => decl,
None => {
let error = if let Some(e) = &environment.as_ref().unwrap().error {
e.clone()
} else {
self.report_error_tree(tree, format!("'{typ}' has no member {id}"))
};
return Some(Type::Error(error));
}
}
}
_ => {
let error = self.report_error_tree_ref(
left_tree,
"cannot assign a value to this expression, it is not a place you can store things",
);
return Some(Type::Error(error));
}
};
match declaration.location {
Location::Argument | Location::Slot | Location::Local | Location::Module => (),
Location::ExternalFunction | Location::Function => {
let error = self.report_error_tree_ref(
left_tree,
"cannot assign a new value to a function declaration",
);
return Some(Type::Error(error));
}
Location::Class => {
let error = self.report_error_tree_ref(
left_tree,
"cannot assign a new value to a class declaration",
);
return Some(Type::Error(error));
}
Location::Import => {
let error = self.report_error_tree_ref(
left_tree,
"cannot assign a new value to an imported module",
);
return Some(Type::Error(error));
}
}
let _ = environment;
let left_type = match left_type {
Type::Assignment(x) => *x,
t => t,
};
let right_type = match right_type {
Type::Assignment(x) => *x,
t => t,
};
if let Type::Error(e) = left_type {
Some(Type::Error(e))
} else if let Type::Error(e) = right_type {
Some(Type::Error(e))
} else if self.can_convert(&right_type, &left_type) {
Some(Type::Assignment(Box::new(left_type)))
} else {
let error = self.report_error_span(
op.start(),
op.end(),
format!("cannot assign a value of type '{right_type}' to type '{left_type}'"),
);
Some(Type::Error(error))
}
}
fn type_of_type_expr(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::TypeExpression);
Some(self.type_of(tree.nth_tree(0)?))
}
fn type_of_type_identifier(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::TypeIdentifier);
// TODO: This will *clearly* need to get better.
let token = self[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)?;
let mut arg_types: Vec<_> = args.child_trees().map(|t| self.type_of(t)).collect();
if arg_types.len() != 1 {
let error = self.report_error_tree(tree, "list takes a single type argument");
Some(Type::Error(error))
} else {
Some(Type::List(Box::new(arg_types.pop().unwrap())))
}
}
_ => {
let environment = self.environment_of(t);
match environment.bind(token) {
Some(declaration) => {
match declaration.location {
Location::Class => Some(self.type_of_declaration(declaration)),
Location::Argument
| Location::Slot
| Location::Local
| Location::Module => {
let error = self.report_error_tree(
tree,
format!("'{token}' is a variable and cannot be used as a type"),
);
Some(Type::Error(error))
}
Location::Function | Location::ExternalFunction => {
let error = self.report_error_tree(
tree,
format!("'{token}' is a function and cannot be used as a type"),
);
Some(Type::Error(error))
}
Location::Import => {
let error = self.report_error_tree(
tree,
format!("'{token}' is an imported module and cannot be used as a type"),
);
Some(Type::Error(error))
}
}
}
None => {
let error = if let Some(e) = &environment.error {
e.clone()
} else {
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"))
};
Some(Type::Error(error))
}
}
}
}
}
fn type_of_type_parameter(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::TypeParameter);
Some(self.type_of(tree.nth_tree(0)?))
}
fn type_of_block(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::Block);
if tree.children.len() < 2 {
return None;
}
if tree.children.len() == 2 {
// Empty blocks generate Nothing.
return Some(Type::Nothing);
}
// The type of the block is the type of the last expression.
// (But the last child is the closing brace probably?)
let last_is_brace = tree.nth_token(tree.children.len() - 1).is_some();
let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 };
let mut is_unreachable = false;
for i in 1..last_index {
// TODO: if `is_unreachable` here then we actually have
// unreachable code here! We should warn about it
// I guess.
is_unreachable =
matches!(self.type_of(tree.nth_tree(i)?), Type::Unreachable) || is_unreachable;
}
// NOTE: If for some reason the last statement is unsuitable for a
// type then we consider the type of the block to be Nothing.
// (And explicitly not Error, which is what returning None
// would yield.)
let last_type = self.type_of(tree.nth_tree(last_index)?);
// If anything in this block generated an "Unreachable" then the
// whole type of the block is "unreachable" no matter what.
Some(if is_unreachable {
Type::Unreachable
} else {
last_type
})
}
fn type_of_literal(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::LiteralExpression);
let tok = &self[tree.nth_token(0)?];
let pig = match tok.kind {
TokenKind::Number => Type::F64,
TokenKind::String => Type::String,
TokenKind::True | TokenKind::False => Type::Bool,
_ => panic!(
"the token {} doesn't have a type!",
tok.as_str(&self.source)
),
};
Some(pig)
}
fn type_of_grouping(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::GroupingExpression);
tree.nth_tree(1).map(|t| self.type_of(t))
}
fn type_of_conditional(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ConditionalExpression);
let then_type = self.type_of(tree.nth_tree(2)?);
let has_else = tree
.nth_token(3)
.map(|t| self[t].kind == TokenKind::Else)
.unwrap_or(false);
let else_type = if has_else {
Some(self.type_of(tree.nth_tree(4)?))
} else {
None
};
match (then_type, else_type) {
(Type::Error(e), _) => Some(Type::Error(e)),
(_, Some(Type::Error(e))) => Some(Type::Error(e)),
(Type::Unreachable, None) => Some(Type::Nothing),
(Type::Unreachable, Some(t)) => Some(t),
(t, Some(Type::Unreachable)) => Some(t),
(then_type, else_type) => {
let else_type = else_type.unwrap_or(Type::Nothing);
Some(self.build_alternate(&then_type, &else_type))
}
}
}
fn type_of_call(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::CallExpression);
// TODO: Move the vast majority of error checking out of this
// function: once you know that the 0th tree (the function
// expression) yields a function type, assume the type of the
// call is the type of the function return. Don't bother
// matching argument types &c; do that in an explicit
// check_call_expression function below.
let f_ref = tree.nth_tree(0)?;
let f = self.type_of(f_ref);
let arg_list = &self[tree.nth_tree(1)?];
let arg_types: Vec<_> = arg_list
.children
.iter()
.filter_map(|c| match c {
Child::Tree(t) => Some((*t, self.type_of(*t))),
_ => None,
})
.collect();
// Propagate type errors if there are any.
let type_error = if let Type::Error(e) = &f {
Some(e.clone())
} else {
arg_types.iter().find_map(|(_, t)| match t {
Type::Error(e) => Some(e.clone()),
_ => None,
})
};
if let Some(error) = type_error {
return Some(Type::Error(error));
}
match f {
Type::Function(params, ret) => {
let mut param_error = None;
if params.len() != arg_types.len() {
// TODO: Augment with function name if known
let err = self
.report_error_tree(tree, format!("expected {} parameters", params.len()));
param_error = Some(err);
}
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
// a here is the type of the argument expression; p is
// the declared type of the parameter.
if !self.can_convert(&a, p) {
let err = self.report_error_tree_ref(
*t,
format!(
"parameter {i} has an incompatible type: expected {} but got {}",
p, a
),
);
param_error = Some(err);
}
}
if let Some(param_error) = param_error {
return Some(Type::Error(param_error));
}
Some(*ret.clone())
}
Type::Method(_, params, ret) => {
let mut param_error = None;
// For the purposes of type checking ignore the self type.
if params.len() != arg_types.len() {
// TODO: Augment with function name if known
let err = self
.report_error_tree(tree, format!("expected {} parameters", params.len()));
param_error = Some(err);
}
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
// a here is the type of the argument expression; p is
// the declared type of the parameter.
if !self.can_convert(&a, p) {
let err = self.report_error_tree_ref(
*t,
format!(
"parameter {i} has an incompatible type: expected {} but got {}",
p, a
),
);
param_error = Some(err);
}
}
if let Some(param_error) = param_error {
return Some(Type::Error(param_error));
}
Some(*ret.clone())
}
_ => {
let err = self
.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}"));
Some(Type::Error(err))
}
}
}
fn type_of_argument(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::Argument);
let result = self.type_of(tree.nth_tree(0)?);
Some(result)
}
fn type_of_member_access(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::MemberAccess);
let lhs = tree.nth_tree(0)?;
let typ = self.type_of(lhs);
let env = self.member_environment(lhs, &typ);
let id = &self[tree.nth_token(2)?];
if id.kind != TokenKind::Identifier {
return Some(self.type_error_for(tree));
}
let id_str = id.as_str(&self.source);
let Some(declaration) = env.bind(id_str) else {
let error = if let Some(e) = &env.error {
e.clone()
} else {
self.report_error_span(
id.start(),
id.end(),
format!("'{typ}' has no member {id_str}"),
)
};
return Some(Type::Error(error));
};
Some(self.type_of_declaration(declaration))
}
pub fn member_environment(&self, t: TreeRef, typ: &Type) -> EnvironmentRef {
match &typ {
Type::Object(mid, ct, _) => {
let class = self.class_of(*mid, *ct);
class.env.clone()
}
Type::Class(mid, ct, _) => {
let class = self.class_of(*mid, *ct);
class.static_env.clone()
}
Type::Module(_, import) => {
// TODO: Cache this somehow, man.
let Some(other) = self.import_by_id(*import) else {
self.internal_compiler_error(Some(t), "Unable to bind module");
};
let Some(root) = other.syntax_tree.root() else {
self.internal_compiler_error(Some(t), "Other syntax tree has no root");
};
let rt = &other[root];
assert_eq!(rt.kind, TreeKind::File);
let mut result = Environment::new(self.mid, None, Location::Module);
let other_env = other.environment_of(root);
for (name, decl) in other_env.declarations.iter() {
if decl.is_exported() {
result.declarations.insert(name.clone(), decl.clone());
}
}
EnvironmentRef::new(result)
}
Type::Error(e) => return Environment::error(e.clone()),
_ => {
let error =
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
return Environment::error(error);
}
}
}
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ExpressionStatement);
let last_is_semicolon = tree
.nth_token(tree.children.len() - 1)
.map(|t| self[t].kind == TokenKind::Semicolon)
.unwrap_or(false);
let expression_type = tree.nth_tree(0).map(|t| self.type_of(t));
match expression_type {
Some(Type::Unreachable) => Some(Type::Unreachable),
_ => {
// A semicolon at the end of an expression statement discards
// the value, leaving us with nothing. (Even if the
// expression otherwise generated a type error!)
if last_is_semicolon {
Some(Type::Nothing)
} else {
expression_type
}
}
}
}
fn type_of_identifier(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::Identifier);
let id = self[tree.nth_token(0)?].as_str(&self.source);
let environment = self.environment_of(t);
if let Some(declaration) = environment.bind(id) {
let typ = self.type_of_declaration(declaration);
// The one weirdsy here is that if this is an identifier that refers
// directly to a class then this should be a *class* type not an
// *object* type.
let typ = if declaration.location == Location::Class {
match typ {
Type::Object(m, t, n) => Type::Class(m, t, n),
_ => self.internal_compiler_error(
Some(t),
"This class declaration did not yield type object!",
),
}
} else {
typ
};
return Some(typ);
}
let error = if let Some(e) = &environment.error {
e.clone()
} else {
self.report_error_tree(tree, format!("cannot find value {id} here"))
};
Some(Type::Error(error))
}
fn type_of_declaration(&self, declaration: &Declaration) -> Type {
match &declaration.origin {
Origin::External(t) => t.clone(),
Origin::Source(t) => {
if declaration.module == self.mid {
self.type_of(*t)
} else {
let Some(other_semantics) = self.import_by_id(declaration.module) else {
let message = format!(
"Cannot find a module matching this decl's mid: {:?}",
declaration.module
);
self.internal_compiler_error(Some(*t), &message);
};
other_semantics.type_of(*t)
}
}
}
}
fn type_of_self_parameter(&self, tree: &Tree) -> Option<Type> {
let pl = tree.parent?;
let param_list = &self[pl];
let fd = param_list.parent?;
let function_decl = &self[fd];
let cd = function_decl.parent?;
let class_decl = &self[cd];
if class_decl.kind != TreeKind::ClassDecl {
let error = self.report_error_tree(tree, "self parameter only allowed in methods");
Some(Type::Error(error))
} else {
Some(self.type_of(cd))
}
}
fn type_of_self_reference(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::SelfReference);
let id = self[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(declaration));
}
let error = if let Some(e) = &environment.error {
e.clone()
} else {
self.report_error_tree(tree, "`self` is only valid in methods")
};
Some(Type::Error(error))
}
fn type_of_function_decl(&self, tree: &Tree) -> Option<Type> {
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
// declaration is not nested in a class, or it is not the first
// parameter.) We could have also chosen to signal it by our
// nesting but we want to extract the self parameter to a
// distinguished place in the function type.
let mut self_type = None;
let mut parameter_types = Vec::new();
for p in param_list.child_trees() {
let p_type = Box::new(self.type_of(p));
if self[p].kind == TreeKind::SelfParameter {
self_type = Some(p_type);
} else {
parameter_types.push(p_type);
}
}
let return_type = match tree.child_of_kind(&self.syntax_tree, TreeKind::ReturnType) {
Some(t) => self.type_of(t),
None => Type::Nothing,
};
let return_type = Box::new(return_type);
Some(match self_type {
Some(self_type) => Type::Method(self_type, parameter_types, return_type),
None => Type::Function(parameter_types, return_type),
})
}
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) {
Some(t) => Some(self.type_of(t)),
None => {
let error =
self.report_error_tree(tree, format!("the parameter is missing a type"));
Some(Type::Error(error))
}
}
}
fn type_of_iterator_variable(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::IteratorVariable);
let parent = &self[tree.parent?];
assert_eq!(parent.kind, TreeKind::ForStatement);
let enumerable = parent.nth_tree(3)?;
let item_type = match self.type_of(enumerable) {
Type::Error(e) => Type::Error(e),
Type::List(x) => (&*x).clone(),
_ => {
let error =
self.report_error_tree_ref(enumerable, "this expression is not enumerable");
Type::Error(error)
}
};
Some(item_type)
}
fn type_of_list_constructor(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ListConstructor);
let mut element_type = None;
for ct in tree.child_trees() {
let child_type = self.type_of(ct);
element_type = match element_type {
None => Some(child_type),
Some(list_type) => {
if list_type.is_error() {
Some(child_type)
} else if child_type.is_error() {
Some(list_type)
} else if self.can_convert(&child_type, &list_type) {
Some(list_type)
} else if self.can_convert(&list_type, &child_type) {
Some(child_type)
} else {
// Even if there is some incompatibility we stick
// with the list type, preferring to guess what was
// intended rather than just error out.
self.report_error_tree_ref(ct, format!("list element of type {child_type} is not compatible with the list type {list_type}"));
Some(list_type)
}
}
}
}
let element_type = element_type.unwrap_or_else(|| Type::TypeVariable(t));
Some(Type::List(Box::new(element_type)))
}
fn type_of_new_object_expression(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::NewObjectExpression);
// NOTE: Matching fields is done in the check function, as is
// reporting on the suitability of the type.
Some(self.type_of(tree.nth_tree(1)?))
}
fn type_of_class_decl(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ClassDecl);
// The details of a class are computed lazily, but this is enough of
// a belly-button.
let name = &self[tree.nth_token(1)?];
// NOTE: There's a kind of a weird design decision here, which is to
// return an instance type instead of a class type. This is
// because it turns out to be what you want most of the time:
// variables should be object type, arguments should be object
// type, etc.
//
// There are only two places where you have to deal with this
// being weird: first, in the new object expression, and
// second, in static member access.
//
// For new object expression it turns out to not matter much,
// you just have to be aware that the type identifier is an
// object type.
//
// For the static member access, that one is just plain weird.
// But it's easier to handle converting object type -> class
// type there (in type_of_identifier) rather than flipping the
// default here and dealing with converting class type ->
// object type literally everywhere else.
Some(Type::Object(self.mid, t, name.as_str(&self.source).into()))
}
fn type_of_field_decl(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::FieldDecl);
// Type of a field declaration is the type of the type expression.
Some(self.type_of(tree.nth_tree(2)?))
}
fn type_of_field_value(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::FieldValue);
if let Some(colon) = tree.nth_token(1) {
if self[colon].kind == TokenKind::Colon {
// Form 1: { x: e, ... }
return Some(self.type_of(tree.nth_tree(2)?));
}
}
// Form 2: { x, ... }
let environment = self.environment_of(t);
let id = self[tree.nth_token(0)?].as_str(&self.source);
let declaration = match environment.bind(id) {
Some(d) => d,
None => {
let error = if let Some(e) = &environment.error {
e.clone()
} else {
self.report_error_tree(tree, format!("cannot find value {id} here"))
};
return Some(Type::Error(error));
}
};
match declaration.location {
Location::Argument
| Location::Slot
| Location::Local
| Location::Module
| Location::Function
| Location::ExternalFunction => Some(self.type_of_declaration(declaration)),
Location::Class => {
let error = self.report_error_tree(
tree,
format!("'{id}' is a class, and cannot be the value of a field"),
);
Some(Type::Error(error))
}
Location::Import => {
let error = self.report_error_tree(
tree,
format!("'{id}' is an imported module, and cannot be the value of a field"),
);
Some(Type::Error(error))
}
}
}
fn type_of_list_constructor_element(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
Some(self.type_of(tree.nth_tree(0)?))
}
fn type_of_return_type(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ReturnType);
Some(self.type_of(tree.nth_tree(1)?)) // type expression
}
fn type_of_if_statement(&self, tree: &Tree) -> Option<Type> {
Some(self.type_of(tree.nth_tree(0)?))
}
fn type_of_alternate_type(&self, tree: &Tree) -> Option<Type> {
// TODO: IDEA: nth_tree returns a bogus tree if not a tree, stop returning Option?
let left = self.type_of(tree.nth_tree(0)?);
let right = self.type_of(tree.nth_tree(2)?);
Some(self.build_alternate(&left, &right))
}
fn type_of_match_expression(&self, tree: &Tree) -> Option<Type> {
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)
.collect();
if arms.len() == 0 {
let error =
self.report_error_tree(tree, "a match expression must have at least one arm");
Some(Type::Error(error))
} else {
let mut actual_type = self.type_of(arms[0]);
for arm in &arms[1..] {
let arm_type = self.type_of(*arm);
if !self.can_convert(&arm_type, &actual_type) {
if self.can_convert(&actual_type, &arm_type) {
// New lowest-common-denominator type
actual_type = arm_type;
} else {
self.report_error_tree_ref(*arm, format!("this arm produces a value of type '{arm_type}' which is incompatible with the general result type {actual_type}"));
}
}
}
Some(actual_type)
}
}
fn type_of_match_arm(&self, tree: &Tree) -> Option<Type> {
// The type of a match arm is the type of the expression to the right.
Some(self.type_of(tree.nth_tree(2)?))
}
fn type_of_while(&self, tree: &Tree) -> Option<Type> {
match self.constant_eval(tree.nth_tree(1)?) {
Some(StackValue::Bool(true)) => Some(Type::Unreachable),
_ => Some(Type::Nothing),
}
}
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)?))
}
fn type_of_import(&self, tree: &Tree) -> Option<Type> {
let tok = &self[tree.nth_token(1)?];
if tok.kind != TokenKind::String {
return Some(self.type_error_for(tree));
}
// do we bind it here? it's not normalized....
let name = string_constant_to_string(tok.as_str(&self.source));
let Some(import_map) = self.import_map.get() else {
self.internal_compiler_error(None, "import map not initialized");
};
match import_map.get(&name) {
Some(import) => Some(Type::Module(name.into(), *import)),
None => {
let error =
self.report_error_tree(tree, format!("unable to resolve module import {name}"));
Some(Type::Error(error))
}
}
}
// TODO: Really want to TEST THIS also uh can we generate bytecode for functions and call it??
fn constant_eval(&self, t: TreeRef) -> Option<StackValue> {
// TODO: Make this cached, incremental, so the compiler can use it for optimizations.
let tree = &self[t];
match tree.kind {
TreeKind::LiteralExpression => {
let tok = &self[tree.nth_token(0)?];
match self.type_of(t) {
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(&self.source)).into(),
)),
Type::Nothing => Some(StackValue::Nothing), // ?
_ => None,
}
}
TreeKind::IsExpression => {
let pt = tree.nth_tree(2)?;
let pattern = &self[pt];
if pattern
.child_of_kind(&self.syntax_tree, TreeKind::WildcardPattern)
.is_some()
{
Some(StackValue::Bool(true))
} else if self.can_convert(&self.type_of(tree.nth_tree(0)?), &self.type_of(pt)) {
Some(StackValue::Bool(true))
} else {
None
}
}
TreeKind::GroupingExpression => self.constant_eval(tree.nth_tree(1)?),
TreeKind::UnaryExpression => {
let op = self[tree.nth_token(0)?].kind;
let val = self.constant_eval(tree.nth_tree(1)?)?;
match (op, val) {
(TokenKind::Plus, StackValue::Float(a)) => Some(StackValue::Float(a)),
(TokenKind::Minus, StackValue::Float(a)) => Some(StackValue::Float(-a)),
(TokenKind::Bang, StackValue::Bool(a)) => Some(StackValue::Bool(!a)),
_ => None,
}
}
TreeKind::BinaryExpression => {
let left = self.constant_eval(tree.nth_tree(0)?)?;
let right = self.constant_eval(tree.nth_tree(2)?)?;
let op = self[tree.nth_token(1)?].kind;
match (op, left, right) {
(TokenKind::Plus, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a + b))
}
(TokenKind::Minus, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a - b))
}
(TokenKind::Star, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a * b))
}
(TokenKind::Slash, StackValue::Float(a), StackValue::Float(b)) => {
if b != 0.0 {
Some(StackValue::Float(a / b))
} else {
None // TODO: Error
}
}
(TokenKind::Plus, StackValue::String(a), StackValue::String(b)) => {
let mut result = String::new();
result.push_str(&*a);
result.push_str(&*b);
Some(StackValue::String(result.into()))
}
(TokenKind::And, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a && b))
}
(TokenKind::Or, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a || b))
}
(TokenKind::EqualEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::Nothing, StackValue::Nothing) => {
Some(StackValue::Bool(true))
}
(TokenKind::Less, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a < b))
}
(TokenKind::LessEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a <= b))
}
(TokenKind::Greater, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a > b))
}
(TokenKind::GreaterEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a >= b))
}
(TokenKind::Less, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a < b))
}
(TokenKind::LessEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a <= b))
}
(TokenKind::Greater, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a > b))
}
(TokenKind::GreaterEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a >= b))
}
_ => None,
}
}
_ => None,
}
}
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
eprintln!("Module: {:?}", self.mid);
eprintln!("Parsed the tree as:");
eprintln!("\n{}", self.syntax_tree.dump(&self.source, true));
{
let errors = self.snapshot_errors();
if errors.len() == 0 {
eprintln!("There were no errors reported during checking.\n");
} else {
eprintln!(
"{} error{} reported during checking:",
errors.len(),
if errors.len() == 1 { "" } else { "s" }
);
for error in errors.iter() {
eprintln!(" Error: {error}");
}
eprintln!();
}
}
{
match self.import_map.get() {
Some(m) => {
eprintln!("Import map:");
for (k, b) in m.iter() {
eprintln!(" {k} => {:?}", b);
}
eprintln!();
}
None => {
eprintln!("The import map is not set.\n")
}
}
eprintln!("Module Table:");
for (id, _sem) in self.module_table.borrow().iter() {
eprintln!(" {:?} => ??", id);
}
eprintln!();
}
if let Some(tr) = tr {
eprintln!("This is about the tree: {:?}", &self[tr]);
eprintln!("The logical parent chain of the tree was:\n");
let mut current = Some(tr);
while let Some(c) = current {
let t = &self[c];
eprintln!(" {:?} [{}-{})", t.kind, t.start_pos, t.end_pos);
current = self.logical_parents[c.index()];
}
eprintln!("\nThe environment of the tree was:");
let mut environment = Some(self.environment_of(tr));
while let Some(env) = environment {
if let Some(error) = &env.error {
eprint!(" *** ERROR: {error}");
}
for (k, v) in env.declarations.iter() {
eprintln!(
" {k}: {:?} {} ({:?} {:?})",
v.location, v.index, v.module, v.origin
);
}
environment = env.parent.clone();
}
eprintln!();
}
}
#[cold]
#[track_caller]
pub fn internal_compiler_error(&self, tr: Option<TreeRef>, message: &str) -> ! {
eprintln!("Internal compiler error: {message}!");
self.dump_compiler_state(tr);
panic!("INTERNAL COMPILER ERROR: {message}")
}
}
pub fn check(s: &Semantics) {
for t in s.syntax_tree.trees() {
let tree = &s[t];
match tree.kind {
TreeKind::Error => {} // already reported
TreeKind::File => {}
TreeKind::FunctionDecl => check_function_decl(s, t, tree),
TreeKind::ParamList => {
let _ = s.environment_of(t);
}
TreeKind::Parameter => {
let _ = s.type_of(t);
}
TreeKind::TypeExpression | TreeKind::AlternateType | TreeKind::TypeIdentifier => {
let _ = s.type_of(t);
}
TreeKind::Block => {
let _ = s.type_of(t);
}
TreeKind::LetStatement => check_let(s, tree),
TreeKind::ReturnStatement => check_return_statement(s, tree),
TreeKind::ExpressionStatement
| TreeKind::LiteralExpression
| TreeKind::GroupingExpression
| TreeKind::UnaryExpression
| TreeKind::BinaryExpression
| TreeKind::MemberAccess => {
let _ = s.type_of(t);
}
TreeKind::ConditionalExpression => check_conditional(s, tree),
TreeKind::CallExpression => {
let _ = s.type_of(t);
}
TreeKind::ArgumentList => {}
TreeKind::Argument => {
let _ = s.type_of(t);
}
TreeKind::IfStatement => {}
TreeKind::Identifier => {
let _ = s.type_of(t);
}
TreeKind::ReturnType => {}
TreeKind::TypeParameter => {}
TreeKind::TypeParameterList => {}
TreeKind::ListConstructor => {
let _ = s.type_of(t);
}
TreeKind::ListConstructorElement => {
let _ = s.type_of(t);
}
TreeKind::ForStatement => check_for_statement(s, t),
TreeKind::IteratorVariable => {}
TreeKind::ClassDecl => check_class_declaration(s, tree),
TreeKind::FieldDecl => {}
TreeKind::FieldList => {}
TreeKind::NewObjectExpression => check_new_object_expression(s, tree),
TreeKind::FieldValue => {}
TreeKind::SelfParameter => {}
TreeKind::SelfReference => {}
TreeKind::IsExpression => {}
TreeKind::VariableBinding => {}
TreeKind::Pattern => check_pattern(s, tree),
TreeKind::WildcardPattern => {}
TreeKind::MatchArm => {}
TreeKind::MatchBody => check_match_body(s, t, tree),
TreeKind::MatchExpression => {}
TreeKind::WhileStatement => check_while_statement(s, tree),
TreeKind::Import => {
let _ = s.type_of(t);
}
TreeKind::Export => {}
TreeKind::ExportList => {
// TODO: Check that each name in the list is in the environment
}
}
}
}
fn check_conditional(s: &Semantics, tree: &Tree) {
let Some(cond_tree) = tree.nth_tree(1) else {
return;
};
let cond_type = s.type_of(cond_tree);
if !s.can_convert(&cond_type, &Type::Bool) {
if !cond_type.is_error() {
s.report_error_tree_ref(
cond_tree,
format!("this condition produces '{cond_type}', but must produce bool"),
);
}
}
}
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 = 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) {
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.
let (start, end) = return_type_tree
.map(|t| {
let rtt = &s[t];
(rtt.start_pos, rtt.end_pos)
})
.unwrap_or_else(|| {
let start = tree.start_pos;
let end_tok = tree
.nth_token(1)
.unwrap_or_else(|| tree.nth_token(0).unwrap());
(start, s[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}'"));
}
}
}
fn check_let(s: &Semantics, tree: &Tree) {
let Some(name) = tree.nth_token(1) else {
return;
};
let name = &s[name];
let Some(expr) = tree.nth_tree(3) else { return };
if let Type::Method(..) = s.type_of(expr) {
s.report_error_span(
name.start(),
name.end(),
"methods cannot be assigned to variables",
);
}
}
fn check_return_statement(s: &Semantics, tree: &Tree) {
assert_eq!(tree.kind, TreeKind::ReturnStatement);
let mut enclosing_function = tree.parent;
while let Some(fp) = enclosing_function {
let fpt = &s[fp];
if fpt.kind == TreeKind::FunctionDecl {
break;
}
enclosing_function = fpt.parent;
}
let Some(enclosing_function) = enclosing_function else {
s.report_error_tree(
tree,
"a return statement can only be used inside a function",
);
return;
};
let function_type = s.type_of(enclosing_function);
match function_type {
Type::Function(_, expected_type) | Type::Method(_, _, expected_type) => {
let actual_type = if let Some(expr) = tree.nth_tree(1) {
s.type_of(expr)
} else {
Type::Nothing
};
if !s.can_convert(&actual_type, &expected_type) {
s.report_error_tree(tree, format!("callers of this function expect a value of type '{expected_type}' but this statement returns a value of type '{actual_type}'"));
}
}
Type::Error(_) => (),
_ => s.internal_compiler_error(
Some(enclosing_function),
"a return statement in here expected this to yield a function type",
),
}
// OK this one is a little bit messed up because it reaches *up*, sorry.
}
fn check_for_statement(s: &Semantics, t: TreeRef) {
let _ = s.environment_of(t);
}
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 {
return;
};
let class_type = s.type_of(type_expression); // TODO: Should yield a ClassType not an ObjectType?
match &class_type {
Type::Object(mid, c, _) => {
// Get the class def from ... place.
let class = s.class_of(*mid, *c);
let mut any_errors = false;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(&s.syntax_tree, TreeKind::FieldValue) {
let f = &s[field];
if let Some(name) = f.nth_token(0) {
let field_type = s.type_of(field);
field_bindings.insert(s[name].as_str(&s.source), (field, field_type));
} else {
any_errors = true;
}
}
// Check individual bindings...
for f in class.fields.iter() {
if let Some((field_tree, expr_type)) = field_bindings.get(&*f.name) {
if !s.can_convert(expr_type, &f.field_type) {
s.report_error_tree_ref(
*field_tree,
format!(
"field {} is of type {}, but this expression generates a {}",
f.name, f.field_type, expr_type,
),
);
}
field_bindings.remove(&*f.name);
} else if !any_errors {
s.report_error_tree(
tree,
format!("missing an initializer for field {}", f.name),
);
}
}
if !any_errors {
for (n, (field_tree, _)) in field_bindings.iter() {
s.report_error_tree_ref(
*field_tree,
format!("{} does not have a field named {}", class_type, n),
);
}
}
}
Type::Error(_) => (),
ct => {
s.report_error_tree_ref(
type_expression,
format!("expected this to be a class type, but it is {ct}"),
);
}
}
}
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) {
let f = &s[field];
let Some(name) = f.nth_token(0) else {
continue;
};
let name = s[name].as_str(&s.source);
match fields.insert(name, field) {
Some(_) => {
s.report_error_tree(f, format!("duplicate definition of field '{name}'"));
}
None => {}
}
}
}
fn check_pattern(s: &Semantics, tree: &Tree) {
// If there's an AND then it must produce a boolean.
let and_index = tree.children.iter().position(|c| match c {
Child::Token(t) => s[t].kind == TokenKind::And,
_ => false,
});
if let Some(and_index) = and_index {
if let Some(pred) = tree.nth_tree(and_index + 1) {
let predicate_type = s.type_of(pred);
if !s.can_convert(&predicate_type, &Type::Bool) {
// TODO: TEST
s.report_error_tree_ref(
pred,
format!("this predicate produces '{predicate_type}', but must produce bool"),
);
}
}
}
// TODO: Warn on constant match
}
fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {
let _ = s.type_of(t); // Checks arm count and compatibility.
// TODO: completeness checks
// 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)
// .collect();
// if arms.len() > 0 {
// for arm in &arms[1..] {
// // TODO: How do I know if it's complete?
// // TODO: How do I know if it's redundant?
// }
// }
}
fn check_while_statement(s: &Semantics, tree: &Tree) {
if let Some(expr) = tree.nth_tree(1) {
let expr_type = s.type_of(expr);
if !s.can_convert(&expr_type, &Type::Bool) {
s.report_error_tree_ref(
expr,
format!("this condition produces '{expr_type}', but must produce bool"),
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse;
#[test]
#[should_panic(expected = "INTERNAL COMPILER ERROR: oh no")]
pub fn ice() {
let source: Rc<str> = "1+1".into();
let (tree, lines) = parse(&source);
let semantics = Semantics::new(ModuleId(0), "__test__".into(), source, tree.clone(), lines);
semantics.internal_compiler_error(tree.root(), "oh no");
}
}