[fine] Gobs of work
- Assertion improvements - Type check function calls - Functions in the environment - Compile function calls
This commit is contained in:
parent
9d226b205d
commit
5ebede4a21
6 changed files with 539 additions and 142 deletions
|
|
@ -57,7 +57,7 @@ impl fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[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
|
||||
|
|
@ -69,12 +69,17 @@ pub enum Type {
|
|||
// everything's fine.
|
||||
Unreachable,
|
||||
|
||||
// This is until generics are working
|
||||
MagicPrintGarbage,
|
||||
|
||||
Nothing,
|
||||
// TODO: Numeric literals should be implicitly convertable, unlike other
|
||||
// types. Maybe just "numeric literal" type?
|
||||
F64,
|
||||
String,
|
||||
Bool,
|
||||
|
||||
Function(Vec<Box<Type>>, Box<Type>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
|
|
@ -119,50 +124,98 @@ impl fmt::Display for Type {
|
|||
F64 => write!(f, "f64"),
|
||||
String => write!(f, "string"),
|
||||
Bool => write!(f, "bool"),
|
||||
MagicPrintGarbage => write!(f, "MagicPrintGarbage"),
|
||||
Function(args, ret) => {
|
||||
write!(f, "(")?;
|
||||
let mut first = true;
|
||||
for arg in args.iter() {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{arg}")?;
|
||||
first = false;
|
||||
}
|
||||
write!(f, ") -> {ret}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Location {
|
||||
Argument,
|
||||
Local,
|
||||
Module,
|
||||
}
|
||||
|
||||
pub struct Declaration {
|
||||
pub declaration_type: Type,
|
||||
pub location: Location,
|
||||
pub index: usize,
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Declaration {
|
||||
Variable {
|
||||
declaration_type: Type,
|
||||
location: Location,
|
||||
index: usize,
|
||||
},
|
||||
Function {
|
||||
declaration_type: Type,
|
||||
declaration: TreeRef, //?
|
||||
},
|
||||
ExternFunction {
|
||||
declaration_type: Type,
|
||||
id: ExternalFunctionId,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Environment {
|
||||
pub parent: Option<EnvironmentRef>,
|
||||
pub location: Location,
|
||||
pub base_index: usize,
|
||||
pub next_index: usize,
|
||||
pub declarations: HashMap<Box<str>, Declaration>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new(parent: Option<EnvironmentRef>, location: Location, base_index: usize) -> Self {
|
||||
pub fn new(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::Local, Location::Local) => base,
|
||||
(_, Location::Local) => 0,
|
||||
|
||||
(Location::Module, Location::Module) => base,
|
||||
(_, Location::Module) => panic!("What?"),
|
||||
};
|
||||
|
||||
Environment {
|
||||
parent,
|
||||
location,
|
||||
base_index,
|
||||
next_index,
|
||||
declarations: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, token: &Token, t: Type) {
|
||||
let index = self.base_index + self.declarations.len();
|
||||
self.declarations.insert(
|
||||
token.as_str().into(),
|
||||
Declaration {
|
||||
Declaration::Variable {
|
||||
declaration_type: t,
|
||||
location: self.location,
|
||||
index,
|
||||
index: self.next_index,
|
||||
},
|
||||
);
|
||||
self.next_index += 1;
|
||||
}
|
||||
|
||||
pub fn bind(&self, token: &Token) -> Option<&Declaration> {
|
||||
|
|
@ -237,6 +290,24 @@ fn set_logical_parents(
|
|||
}
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// By default, the parent for each child is current tree.
|
||||
for child in &tree.children {
|
||||
|
|
@ -270,7 +341,7 @@ pub struct Semantics<'a> {
|
|||
errors: RefCell<Vec<Error>>,
|
||||
types: RefCell<Vec<Incremental<Type>>>,
|
||||
environments: RefCell<Vec<Incremental<EnvironmentRef>>>,
|
||||
empty_environment: EnvironmentRef,
|
||||
root_environment: EnvironmentRef,
|
||||
}
|
||||
|
||||
impl<'a> Semantics<'a> {
|
||||
|
|
@ -280,6 +351,15 @@ impl<'a> Semantics<'a> {
|
|||
set_logical_parents(&mut logical_parents, tree, root, None);
|
||||
}
|
||||
|
||||
let mut root_environment = Environment::new(None, Location::Module);
|
||||
root_environment.declarations.insert(
|
||||
"print".into(),
|
||||
Declaration::ExternFunction {
|
||||
declaration_type: Type::MagicPrintGarbage,
|
||||
id: ExternalFunctionId(0),
|
||||
},
|
||||
);
|
||||
|
||||
let mut semantics = Semantics {
|
||||
syntax_tree: tree,
|
||||
lines,
|
||||
|
|
@ -287,7 +367,7 @@ impl<'a> Semantics<'a> {
|
|||
errors: RefCell::new(vec![]),
|
||||
types: RefCell::new(vec![Incremental::None; tree.len()]),
|
||||
environments: RefCell::new(vec![Incremental::None; tree.len()]),
|
||||
empty_environment: EnvironmentRef::new(Environment::new(None, Location::Module, 0)),
|
||||
root_environment: EnvironmentRef::new(root_environment),
|
||||
};
|
||||
|
||||
// NOTE: We ensure all the known errors are reported before we move
|
||||
|
|
@ -379,7 +459,7 @@ impl<'a> Semantics<'a> {
|
|||
Incremental::Complete(e) => return e.clone(),
|
||||
Incremental::InProgress => {
|
||||
// NOTE: Set the state so the ICE doesn't loop on itself.
|
||||
*state = Incremental::Complete(self.empty_environment.clone());
|
||||
*state = Incremental::Complete(self.root_environment.clone());
|
||||
drop(borrow);
|
||||
|
||||
//eprintln!("environment_of circular => {t:?}");
|
||||
|
|
@ -394,12 +474,14 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
let parent = match self.logical_parents[t.index()] {
|
||||
Some(t) => self.environment_of(t),
|
||||
None => self.empty_environment.clone(),
|
||||
None => self.root_environment.clone(),
|
||||
};
|
||||
|
||||
let result = match tree.kind {
|
||||
TreeKind::LetStatement => self.environment_of_let(parent, tree),
|
||||
TreeKind::FunctionDecl => self.environment_of_func(parent, tree),
|
||||
TreeKind::ParamList => self.environment_of_paramlist(parent, tree),
|
||||
TreeKind::File => self.environment_of_file(parent, tree),
|
||||
TreeKind::Block => self.environment_of_block(parent, tree),
|
||||
|
||||
// TODO: Blocks should introduce a local environment if required.
|
||||
// Test with a type error in a block statement and a
|
||||
|
|
@ -426,6 +508,62 @@ impl<'a> Semantics<'a> {
|
|||
result
|
||||
}
|
||||
|
||||
fn environment_of_block(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
let mut environment = Environment::new(Some(parent), Location::Local);
|
||||
for child in tree.children.iter() {
|
||||
match child {
|
||||
Child::Tree(t) => {
|
||||
let ct = &self.syntax_tree[*t];
|
||||
if ct.kind == TreeKind::FunctionDecl {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
EnvironmentRef::new(environment)
|
||||
}
|
||||
|
||||
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];
|
||||
if ct.kind == TreeKind::FunctionDecl {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
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?
|
||||
|
|
@ -440,35 +578,23 @@ impl<'a> Semantics<'a> {
|
|||
None => Type::Error,
|
||||
};
|
||||
|
||||
let (location, base_index) = match parent.location {
|
||||
Location::Local => (
|
||||
Location::Local,
|
||||
parent.base_index + parent.declarations.len(),
|
||||
),
|
||||
Location::Module => (
|
||||
Location::Module,
|
||||
parent.base_index + parent.declarations.len(),
|
||||
),
|
||||
Location::Argument => (Location::Local, 0),
|
||||
let location = match parent.location {
|
||||
Location::Local => Location::Local,
|
||||
Location::Module => Location::Module,
|
||||
Location::Argument => Location::Local,
|
||||
};
|
||||
|
||||
let mut environment = Environment::new(Some(parent), location, base_index);
|
||||
let mut environment = Environment::new(Some(parent), location);
|
||||
environment.insert(name, declaration_type);
|
||||
|
||||
EnvironmentRef::new(environment)
|
||||
}
|
||||
|
||||
fn environment_of_func(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
let Some(param_list) = tree.nth_tree(2) else {
|
||||
return parent; // SE
|
||||
};
|
||||
let param_list = &self.syntax_tree[param_list];
|
||||
if param_list.kind != TreeKind::ParamList {
|
||||
return parent; // SE
|
||||
}
|
||||
fn environment_of_paramlist(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
assert!(tree.kind == TreeKind::ParamList);
|
||||
|
||||
let mut environment = Environment::new(Some(parent), Location::Argument, 0);
|
||||
for child in param_list.children.iter() {
|
||||
let mut environment = Environment::new(Some(parent), Location::Argument);
|
||||
for child in tree.children.iter() {
|
||||
let Child::Tree(ct) = child else {
|
||||
continue;
|
||||
};
|
||||
|
|
@ -529,12 +655,18 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
|
||||
TreeKind::Identifier => self.type_of_identifier(t, tree),
|
||||
|
||||
TreeKind::FunctionDecl => self.type_of_function_decl(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(Type::Error);
|
||||
|
||||
if result.is_error() {
|
||||
eprintln!("OH NO AN ERROR AT {}: {:?}", t.index(), tree);
|
||||
}
|
||||
|
||||
self.types.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
|
||||
result
|
||||
}
|
||||
|
|
@ -635,7 +767,10 @@ impl<'a> Semantics<'a> {
|
|||
"string" => Some(Type::String),
|
||||
"bool" => Some(Type::Bool),
|
||||
"()" => Some(Type::Nothing),
|
||||
_ => Some(Type::Error),
|
||||
_ => {
|
||||
self.report_error_tree(tree, "Unrecognized type");
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -748,12 +883,64 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
fn type_of_call(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::CallExpression);
|
||||
Some(Type::Error)
|
||||
|
||||
let f_ref = tree.nth_tree(0)?;
|
||||
let f = self.type_of(f_ref);
|
||||
|
||||
let arg_list = &self.syntax_tree[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();
|
||||
|
||||
if f.is_error() || arg_types.iter().any(|(_, t)| t.is_error()) {
|
||||
return Some(Type::Error);
|
||||
}
|
||||
|
||||
match f {
|
||||
Type::Function(params, ret) => {
|
||||
let mut any_errors = false;
|
||||
if params.len() != arg_types.len() {
|
||||
// TODO: Augment with function name if known
|
||||
self.report_error_tree(tree, format!("expected {} parameters", params.len()));
|
||||
any_errors = true;
|
||||
}
|
||||
|
||||
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
|
||||
if !a.compatible_with(p) {
|
||||
self.report_error_tree_ref(
|
||||
*t,
|
||||
format!(
|
||||
"parameter {i} has an incompatible type: expected {} but got {}",
|
||||
p, a
|
||||
),
|
||||
);
|
||||
any_errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if any_errors {
|
||||
return Some(Type::Error);
|
||||
}
|
||||
|
||||
Some(*ret.clone())
|
||||
}
|
||||
_ => {
|
||||
self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}"));
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_argument(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::Argument);
|
||||
Some(Type::Error)
|
||||
|
||||
let result = self.type_of(tree.nth_tree(0)?);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
|
||||
|
|
@ -785,13 +972,42 @@ impl<'a> Semantics<'a> {
|
|||
let id = tree.nth_token(0)?;
|
||||
let environment = self.environment_of(t);
|
||||
if let Some(declaration) = environment.bind(id) {
|
||||
return Some(declaration.declaration_type);
|
||||
return Some(match declaration {
|
||||
Declaration::Variable {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Function {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
Some(Type::Error)
|
||||
}
|
||||
|
||||
fn type_of_function_decl(&self, tree: &Tree) -> Option<Type> {
|
||||
let param_list = tree.child_tree_of_kind(self.syntax_tree, TreeKind::ParamList)?;
|
||||
let mut parameter_types = Vec::new();
|
||||
for p in param_list.child_trees() {
|
||||
let param = &self.syntax_tree[p];
|
||||
|
||||
// TODO: Missing type expression means it's a generic function.
|
||||
let parameter_type = param.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?;
|
||||
parameter_types.push(Box::new(self.type_of(parameter_type)));
|
||||
}
|
||||
|
||||
let return_type = match tree.child_tree_of_kind(self.syntax_tree, TreeKind::ReturnType) {
|
||||
Some(t) => self.type_of(t.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?),
|
||||
None => Type::Nothing,
|
||||
};
|
||||
let return_type = Box::new(return_type);
|
||||
Some(Type::Function(parameter_types, return_type))
|
||||
}
|
||||
|
||||
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
|
||||
eprintln!("Parsed the tree as:");
|
||||
eprintln!("\n{}", self.syntax_tree.dump(true));
|
||||
|
|
@ -826,7 +1042,28 @@ impl<'a> Semantics<'a> {
|
|||
let mut environment = Some(self.environment_of(tr));
|
||||
while let Some(env) = environment {
|
||||
for (k, v) in env.declarations.iter() {
|
||||
eprintln!(" {k}: {:?}", v.declaration_type);
|
||||
eprint!(" {k}: ");
|
||||
match v {
|
||||
Declaration::Variable {
|
||||
declaration_type,
|
||||
location,
|
||||
index,
|
||||
} => {
|
||||
eprintln!("{declaration_type:?} (variable {location:?} {index})");
|
||||
}
|
||||
Declaration::Function {
|
||||
declaration_type,
|
||||
declaration,
|
||||
} => {
|
||||
eprintln!("{declaration_type:?} (function {declaration:?})");
|
||||
}
|
||||
Declaration::ExternFunction {
|
||||
declaration_type,
|
||||
id,
|
||||
} => {
|
||||
eprintln!("{declaration_type:?} (extern {id:?})");
|
||||
}
|
||||
};
|
||||
}
|
||||
environment = env.parent.clone();
|
||||
}
|
||||
|
|
@ -872,9 +1109,14 @@ pub fn check(s: &Semantics) {
|
|||
let _ = s.type_of(t);
|
||||
}
|
||||
TreeKind::ArgumentList => {}
|
||||
TreeKind::Argument => {}
|
||||
TreeKind::Argument => {
|
||||
let _ = s.type_of(t);
|
||||
}
|
||||
TreeKind::IfStatement => {}
|
||||
TreeKind::Identifier => {}
|
||||
TreeKind::Identifier => {
|
||||
let _ = s.type_of(t);
|
||||
}
|
||||
TreeKind::ReturnType => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue