typeck: Begin implementing scoping rules
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
//! Tests the lexer\
|
//! Tests the lexer\
|
||||||
use doughlang::{
|
use doughlang::{
|
||||||
ast::{
|
ast::{
|
||||||
Anno, Annotation, Bind, Expr, Pat, Use,
|
Anno, Annotation, Bind, DefaultTypes, Expr, Pat, Use,
|
||||||
macro_matcher::{Match, Subst},
|
macro_matcher::{Match, Subst},
|
||||||
|
visit::Walk,
|
||||||
},
|
},
|
||||||
lexer::{EOF, LexError, Lexer},
|
lexer::{EOF, LexError, Lexer},
|
||||||
parser::{Parse, ParseError, Parser},
|
parser::{Parse, ParseError, Parser},
|
||||||
span::Span,
|
span::Span,
|
||||||
token::{TKind, Token},
|
token::{TKind, Token},
|
||||||
|
typeck::Collector,
|
||||||
};
|
};
|
||||||
use repline::prebaked::*;
|
use repline::prebaked::*;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -25,6 +27,7 @@ enum Verbosity {
|
|||||||
#[default]
|
#[default]
|
||||||
Pretty,
|
Pretty,
|
||||||
Debug,
|
Debug,
|
||||||
|
Frob,
|
||||||
Quiet,
|
Quiet,
|
||||||
}
|
}
|
||||||
impl From<&str> for Verbosity {
|
impl From<&str> for Verbosity {
|
||||||
@@ -32,6 +35,7 @@ impl From<&str> for Verbosity {
|
|||||||
match value {
|
match value {
|
||||||
"quiet" | "false" | "0" | "no" => Verbosity::Quiet,
|
"quiet" | "false" | "0" | "no" => Verbosity::Quiet,
|
||||||
"debug" | "d" => Verbosity::Debug,
|
"debug" | "d" => Verbosity::Debug,
|
||||||
|
"frob" => Verbosity::Frob,
|
||||||
"pretty" => Verbosity::Pretty,
|
"pretty" => Verbosity::Pretty,
|
||||||
_ => Default::default(),
|
_ => Default::default(),
|
||||||
}
|
}
|
||||||
@@ -99,7 +103,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
println!("Parse mode set to '{parsing:?}'");
|
println!("Parse mode set to '{parsing:?}'");
|
||||||
Ok(Response::Accept)
|
Ok(Response::Accept)
|
||||||
}
|
}
|
||||||
line @ ("quiet" | "debug" | "pretty") => {
|
line @ ("quiet" | "debug" | "frob" | "pretty") => {
|
||||||
verbose = Verbosity::from(line);
|
verbose = Verbosity::from(line);
|
||||||
println!("Verbosity set to '{verbose:?}'");
|
println!("Verbosity set to '{verbose:?}'");
|
||||||
Ok(Response::Accept)
|
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));
|
let mut parser = Parser::new(Lexer::new(document));
|
||||||
for idx in 0.. {
|
for idx in 0.. {
|
||||||
match (parser.parse::<Anno<T, _>>(T::Prec::default()), verbose) {
|
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) => {
|
(Ok(Anno(expr, span)), Verbosity::Pretty) => {
|
||||||
println!("\x1b[{}m{span}:\n{expr}", (idx + 5) % 6 + 31);
|
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) => {
|
(Ok(Anno(expr, span)), Verbosity::Debug) => {
|
||||||
println!("\x1b[{}m{span}:\n{expr:#?}", (idx + 5) % 6 + 31);
|
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 {
|
pub mod typed_ast {
|
||||||
//! The Typed AST defines an interface between the type checker and code generator
|
//! The Typed AST defines an interface between the type checker and code generator
|
||||||
|
|
||||||
use crate::span::Span;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
@@ -29,16 +27,278 @@ pub mod typed_ast {
|
|||||||
pub local: HashMap<usize, isize>,
|
pub local: HashMap<usize, isize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `DefID` annotation
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
pub enum Vis {
|
||||||
pub struct Defn {
|
#[default]
|
||||||
pub span: Span,
|
Private,
|
||||||
/// The index of this name in the associated Table
|
Public,
|
||||||
pub defid: usize,
|
}
|
||||||
|
|
||||||
|
#[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 {
|
pub mod ir {
|
||||||
//! The IR defines an interface between the code generator and interpreter(?)
|
//! The IR defines an interface between the code generator and interpreter(?)
|
||||||
|
|||||||
Reference in New Issue
Block a user