typeck: Begin implementing scoping rules

This commit is contained in:
2026-01-06 05:57:50 -05:00
parent 2be73d2660
commit a7ebf31878
2 changed files with 283 additions and 12 deletions

View File

@@ -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);
}

View File

@@ -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(?)