typeck: Begin implementing scoping rules
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
//! Tests the lexer\
|
||||
use doughlang::{
|
||||
ast::{
|
||||
Anno, Annotation, Bind, Expr, Pat, Use,
|
||||
Anno, Annotation, Bind, DefaultTypes, Expr, Pat, Use,
|
||||
macro_matcher::{Match, Subst},
|
||||
visit::Walk,
|
||||
},
|
||||
lexer::{EOF, LexError, Lexer},
|
||||
parser::{Parse, ParseError, Parser},
|
||||
span::Span,
|
||||
token::{TKind, Token},
|
||||
typeck::Collector,
|
||||
};
|
||||
use repline::prebaked::*;
|
||||
use std::{
|
||||
@@ -25,6 +27,7 @@ enum Verbosity {
|
||||
#[default]
|
||||
Pretty,
|
||||
Debug,
|
||||
Frob,
|
||||
Quiet,
|
||||
}
|
||||
impl From<&str> for Verbosity {
|
||||
@@ -32,6 +35,7 @@ impl From<&str> for Verbosity {
|
||||
match value {
|
||||
"quiet" | "false" | "0" | "no" => Verbosity::Quiet,
|
||||
"debug" | "d" => Verbosity::Debug,
|
||||
"frob" => Verbosity::Frob,
|
||||
"pretty" => Verbosity::Pretty,
|
||||
_ => Default::default(),
|
||||
}
|
||||
@@ -99,7 +103,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("Parse mode set to '{parsing:?}'");
|
||||
Ok(Response::Accept)
|
||||
}
|
||||
line @ ("quiet" | "debug" | "pretty") => {
|
||||
line @ ("quiet" | "debug" | "frob" | "pretty") => {
|
||||
verbose = Verbosity::from(line);
|
||||
println!("Verbosity set to '{verbose:?}'");
|
||||
Ok(Response::Accept)
|
||||
@@ -194,7 +198,10 @@ fn tokens<'t, T: Parse<'t> + ?Sized>(document: &'t str, verbose: Verbosity) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse<'t, T: Parse<'t> + Annotation>(document: &'t str, verbose: Verbosity) {
|
||||
fn parse<'t, T: Parse<'t> + Annotation + for<'a> Walk<'a, DefaultTypes>>(
|
||||
document: &'t str,
|
||||
verbose: Verbosity,
|
||||
) {
|
||||
let mut parser = Parser::new(Lexer::new(document));
|
||||
for idx in 0.. {
|
||||
match (parser.parse::<Anno<T, _>>(T::Prec::default()), verbose) {
|
||||
@@ -223,6 +230,10 @@ fn parse<'t, T: Parse<'t> + Annotation>(document: &'t str, verbose: Verbosity) {
|
||||
(Ok(Anno(expr, span)), Verbosity::Pretty) => {
|
||||
println!("\x1b[{}m{span}:\n{expr}", (idx + 5) % 6 + 31);
|
||||
}
|
||||
(Ok(Anno(expr, span)), Verbosity::Frob) => {
|
||||
println!("\x1b[{}m{span}:\n", (idx + 5) % 6 + 31);
|
||||
let _ = expr.visit_in(&mut Collector::new());
|
||||
}
|
||||
(Ok(Anno(expr, span)), Verbosity::Debug) => {
|
||||
println!("\x1b[{}m{span}:\n{expr:#?}", (idx + 5) % 6 + 31);
|
||||
}
|
||||
|
||||
278
src/lib.rs
278
src/lib.rs
@@ -18,8 +18,6 @@ pub mod parser;
|
||||
|
||||
pub mod typed_ast {
|
||||
//! The Typed AST defines an interface between the type checker and code generator
|
||||
|
||||
use crate::span::Span;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Table {
|
||||
@@ -29,16 +27,278 @@ pub mod typed_ast {
|
||||
pub local: HashMap<usize, isize>,
|
||||
}
|
||||
|
||||
/// `DefID` annotation
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Defn {
|
||||
pub span: Span,
|
||||
/// The index of this name in the associated Table
|
||||
pub defid: usize,
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Vis {
|
||||
#[default]
|
||||
Private,
|
||||
Public,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Mut {
|
||||
#[default]
|
||||
Not,
|
||||
Mut,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Binding {
|
||||
#[default]
|
||||
Const,
|
||||
Static,
|
||||
Local,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod typeck {}
|
||||
pub mod typeck {
|
||||
use crate::{
|
||||
ast::{
|
||||
types::Symbol,
|
||||
visit::{Visit, Walk},
|
||||
*,
|
||||
},
|
||||
typed_ast::{Binding, Mut, Vis},
|
||||
};
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- Make Expr polymorphic over the Path type
|
||||
- Construct disjoint set forest of names
|
||||
- Replace names with their resolved module identifiers
|
||||
|
||||
|
||||
Pattern matching
|
||||
|
||||
A ::= Decision trees
|
||||
Leaf(k) success (k is an action, an integer)
|
||||
Fail failure
|
||||
Switch(L) multi-way test (o is an occurrence)
|
||||
Swap(A) stack swap (i is an integer)
|
||||
|
||||
|
||||
|
||||
What information do I need to collect before type inference can be run?
|
||||
1. Where all the items are
|
||||
2. Where all the imports link to
|
||||
|
||||
How do I represent that information?
|
||||
|
||||
- Directed graph?
|
||||
|
||||
How do I represent a *lazy* back edge?
|
||||
*/
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum List<'parent, T> {
|
||||
Cons(&'parent List<'parent, T>, T),
|
||||
#[default]
|
||||
Nil,
|
||||
}
|
||||
|
||||
impl<'parent, T> List<'parent, T> {
|
||||
pub fn new(value: T) -> List<'static, T> {
|
||||
List::Cons(&List::Nil, value)
|
||||
}
|
||||
|
||||
pub fn enter<'a>(&'a self, value: T) -> List<'a, T>
|
||||
where T: 'a {
|
||||
List::Cons(self, value)
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&List<'parent, T>> {
|
||||
match self {
|
||||
Self::Cons(parent, _) => Some(parent),
|
||||
Self::Nil => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Path<'parent> = List<'parent, Symbol>;
|
||||
|
||||
impl From<Path<'_>> for crate::ast::types::Path {
|
||||
fn from(value: Path<'_>) -> Self {
|
||||
fn inner(path: &Path<'_>, vec: &mut Vec<Symbol>) {
|
||||
match path {
|
||||
List::Nil => {}
|
||||
&List::Cons(nested, name) => {
|
||||
inner(nested, vec);
|
||||
vec.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parts = vec![];
|
||||
inner(&value, &mut parts);
|
||||
Self { parts }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Path<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Cons(Self::Nil, name) => name.fmt(f),
|
||||
Self::Cons(parent, name) => write!(f, "{parent}::{name}"),
|
||||
Self::Nil => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Collector<'parent, 'source, A: AstTypes> {
|
||||
pub path: List<'parent, &'parent A::Symbol>,
|
||||
pub meta: List<'parent, &'source Expr<A>>,
|
||||
pub visible: Vis,
|
||||
pub mutable: Mut,
|
||||
pub binding: Binding,
|
||||
}
|
||||
|
||||
impl<'parent, 'source, A: AstTypes> Default for Collector<'parent, 'source, A> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
path: Default::default(),
|
||||
meta: Default::default(),
|
||||
visible: Default::default(),
|
||||
mutable: Default::default(),
|
||||
binding: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'source, A: AstTypes> Collector<'a, 'source, A> {
|
||||
pub fn new() -> Collector<'static, 'static, A> {
|
||||
Collector::default()
|
||||
}
|
||||
|
||||
pub fn scope<'b>(&'b self, name: &'b A::Symbol) -> Collector<'b, 'source, A> {
|
||||
Collector { path: self.path.enter(name), ..*self }
|
||||
}
|
||||
|
||||
pub fn module<'b>(&'b self, part: &'b A::Symbol) -> Collector<'b, 'source, A> {
|
||||
let Collector { path, .. } = self;
|
||||
Collector { path: path.enter(part), binding: Binding::Const, ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn function<'b>(&'b self, part: &'b A::Symbol) -> Collector<'b, 'source, A> {
|
||||
let Collector { path, .. } = self;
|
||||
Collector { path: path.enter(part), binding: Binding::Local, ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn meta<'e>(&self, expr: &'e Expr<A>) -> Collector<'_, 'e, A>
|
||||
where 'source: 'e {
|
||||
let &Self { path, ref meta, .. } = self;
|
||||
Collector { path, meta: meta.enter(expr), ..*self }
|
||||
}
|
||||
|
||||
pub fn visible(&self, visible: Vis) -> Self {
|
||||
Self { visible, ..*self }
|
||||
}
|
||||
pub fn mutable(&self, mutable: Mut) -> Self {
|
||||
Self { mutable, ..*self }
|
||||
}
|
||||
pub fn bindmode(&self, binding: Binding) -> Self {
|
||||
Self { binding, ..*self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AstTypes> std::fmt::Display for Collector<'_, '_, A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { path, meta, visible, mutable, binding } = self;
|
||||
|
||||
fn walk_meta<A: AstTypes>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
meta: &List<'_, &Expr<A>>,
|
||||
) -> std::fmt::Result {
|
||||
match meta {
|
||||
List::Cons(nested, expr) => {
|
||||
walk_meta(f, nested)?;
|
||||
writeln!(f, "{expr}")
|
||||
}
|
||||
List::Nil => Ok(()),
|
||||
}
|
||||
}
|
||||
fn walk_path<D: std::fmt::Display>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
meta: &List<'_, &D>,
|
||||
) -> std::fmt::Result {
|
||||
match meta {
|
||||
List::Cons(nested, expr) => {
|
||||
walk_path(f, nested)?;
|
||||
write!(f, "::{expr}")
|
||||
}
|
||||
List::Nil => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
walk_meta(f, meta)?;
|
||||
match binding {
|
||||
Binding::Const => "const ",
|
||||
Binding::Static => "static ",
|
||||
Binding::Local => "local ",
|
||||
}
|
||||
.fmt(f)?;
|
||||
|
||||
match visible {
|
||||
Vis::Private => "",
|
||||
Vis::Public => "pub ",
|
||||
}
|
||||
.fmt(f)?;
|
||||
|
||||
match mutable {
|
||||
Mut::Not => "",
|
||||
Mut::Mut => "mut ",
|
||||
}
|
||||
.fmt(f)?;
|
||||
|
||||
walk_path(f, path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Visit<'b, DefaultTypes> for Collector<'_, '_, DefaultTypes> {
|
||||
type Error = ();
|
||||
|
||||
fn visit_expr(&mut self, expr: &'b Expr) -> Result<(), Self::Error> {
|
||||
match expr {
|
||||
Expr::Op(Op::Pub, exprs) => self.visible(Vis::Public).visit(exprs),
|
||||
Expr::Op(Op::Const, exprs) => self.bindmode(Binding::Const).visit(exprs),
|
||||
Expr::Op(Op::Static, exprs) => self.bindmode(Binding::Static).visit(exprs),
|
||||
Expr::Op(Op::Meta, exprs) => match exprs.as_slice() {
|
||||
[Anno(meta, _), exprs @ ..] => self.meta(meta).visit(exprs),
|
||||
[] => Ok(()),
|
||||
},
|
||||
_ => expr.children(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_bind(&mut self, item: &'b Bind) -> Result<(), Self::Error> {
|
||||
if let Bind(BindOp::Match | BindOp::Impl, _, pat, ..) = item {
|
||||
// TODO: convert patterns into concrete type annotations
|
||||
return item.children(&mut self.scope(&pat.to_string().as_str().into()));
|
||||
}
|
||||
println!("{self}::{}", item.2);
|
||||
|
||||
match item {
|
||||
Bind(BindOp::Fn, _, Pat::Op(PatOp::TypePrefixed, pats), exprs) => {
|
||||
match pats.as_slice() {
|
||||
[Pat::Name(name), ..] => self.function(name).visit(exprs),
|
||||
[other, ..] => {
|
||||
println!("Warning: unhandled fn pattern: {other}");
|
||||
Ok(())
|
||||
}
|
||||
[] => Ok(()),
|
||||
}
|
||||
}
|
||||
Bind(BindOp::Mod, _, Pat::Name(name), exprs) => self.module(name).visit(exprs),
|
||||
_ => item.children(
|
||||
&mut self
|
||||
.visible(Vis::Private)
|
||||
.scope(&Symbol::from(format!("{:?}", item.0).as_str())),
|
||||
),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ir {
|
||||
//! The IR defines an interface between the code generator and interpreter(?)
|
||||
|
||||
Reference in New Issue
Block a user