Update how visitors work, add NameId type

* Visitors are now defined on a per-type level, allowing for greater
  flexibility in combining and re-using behavior
* NameId is used for namespaces, which are used to index locally scoped
  variables. Syms are used for free namespaces, specifically in objects.
  All NameIDs are symbols, while not all symbols are NameIDs.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-05-20 14:41:12 -04:00
parent 3ce7384f15
commit 32591f5e29
15 changed files with 306 additions and 206 deletions

View File

@@ -1,6 +1,69 @@
use crate::compile::ir::Body;
use crate::{
syn::{ast::*, op::BinOp, span::*},
compile::{
Ctx,
error::*,
ir,
visit::*,
},
};
// basic block
pub enum Block {
Body(Body),
Body(ir::Body),
}
pub struct TranslateAst<'c, 't> {
ctx: &'c mut Ctx,
text: &'t str,
}
impl<'c, 't> TranslateAst<'c, 't> {
pub fn new(ctx: &'c mut Ctx, text: &'t str) -> Self {
TranslateAst {
ctx,
text,
}
}
pub fn translate(&mut self, ast: &Vec<Stmt>) -> Result<ir::Body> {
todo!()
}
fn visit_lhs_expr(&mut self, expr: &Expr) -> Result<ir::Lhs> {
match expr {
Expr::Bin(b) if b.op == BinOp::Dot => todo!(),
Expr::Base(BaseExpr { kind: BaseExprKind::Ident, .. }) => {
let name = expr.text_at(self.text);
//let name_id = self.ctx.
todo!()
//Ok(ir::Lhs::Name(
}
_ => todo!()
}
}
}
impl Visit<Stmt> for TranslateAst<'_, '_> {
type Out = Result<ir::Stmt>;
fn visit(&mut self, stmt: &Stmt) -> Self::Out {
todo!()
}
}
impl Visit<AssignStmt> for TranslateAst<'_, '_> {
type Out = Result<ir::Stmt>;
fn visit(&mut self, stmt: &AssignStmt) -> Self::Out {
todo!()
}
}
impl Visit<Expr> for TranslateAst<'_, '_> {
type Out = Result<ir::Expr>;
fn visit(&mut self, expr: &Expr) -> Self::Out {
todo!()
}
}

View File

@@ -1,9 +1,12 @@
use crate::syn::span::*;
use snafu::Snafu;
#[derive(Debug, Snafu)]
pub enum Error {
//#[snafu(display("illegal attr key"))]
#[snafu(display("invalid assignment target"))]
InvalidLhs {
span: Span,
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

View File

@@ -1,5 +1,4 @@
use crate::{
compile::Ctx,
obj::Sym,
syn::span::*,
};
@@ -14,7 +13,7 @@ pub enum Stmt {
#[derive(Debug, Clone)]
pub enum Lhs {
Sym(Sym),
Name(Sym),
Complex(Expr),
}
@@ -29,7 +28,7 @@ pub enum BaseExprKind {
Num(i64),
Str(String),
Sym(Sym),
Ident(String),
Ident(Sym),
}
#[derive(Debug, Clone)]
@@ -37,23 +36,3 @@ pub struct BaseExpr {
pub kind: BaseExprKind,
pub span: Span,
}
// FromAst and IntoAst are just so we can use .into_ast(ctx, text)
// some impls may not need ctx &mut ref - but probably needed for symbol resolution
pub trait FromAst<A> {
fn from_ast(other: A, ctx: &mut Ctx, text: &str) -> Self;
}
pub trait IntoIr<I> {
fn into_ir(self, ctx: &mut Ctx, text: &str) -> I;
}
impl<A, I> IntoIr<I> for A
where
I: FromAst<A>,
{
fn into_ir(self, ctx: &mut Ctx, text: &str) -> I {
FromAst::from_ast(self, ctx, text)
}
}

View File

@@ -1,8 +1,8 @@
#[macro_use] pub mod visit;
pub mod block;
pub mod error;
pub mod ir;
pub mod name;
pub mod visit;
use crate::compile::name::NameStack;
@@ -10,23 +10,17 @@ use crate::compile::name::NameStack;
// * Collect names as symbols
// * Create basic blocks
pub struct Ctx<'t> {
text: &'t str,
pub struct Ctx {
name_stack: NameStack,
}
impl<'t> Ctx<'t> {
pub fn new(text: &'t str) -> Self {
impl Ctx {
pub fn new() -> Self {
Ctx {
text,
name_stack: NameStack::new(),
}
}
pub fn text(&self) -> &'t str {
self.text
}
pub fn name_stack(&self) -> &NameStack {
&self.name_stack
}
@@ -35,16 +29,3 @@ impl<'t> Ctx<'t> {
&mut self.name_stack
}
}
// no sugar in the syntax quite yet
/*
pub struct Desugar;
impl Pass<Vec<Stmt>> for Desugar {
type Out = Vec<Stmt>;
fn pass(&mut self, input: Vec<Stmt>) -> Vec<Stmt> {
input
}
}
*/

View File

@@ -1,22 +1,10 @@
use crate::{
compile::{visit::*, Ctx},
syn::{ast::prelude::*, op::BinOp, span::*},
obj::prelude::*,
syn::{ast::prelude::*, span::*},
};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct NameId(usize);
impl NameId {
pub fn new(id: usize) -> Self {
NameId(id)
}
pub fn id(&self) -> usize {
self.0
}
}
pub struct NameStack {
next_sym: usize,
stack: Vec<HashMap<String, NameId>>,
@@ -84,78 +72,70 @@ impl NameStack {
}
/// Collect local names and push them to the top layer of the name stack.
pub struct CollectNames<'c, 't: 'c> {
ctx: &'c mut Ctx<'t>,
pub struct CollectNames<'c, 't> {
ctx: &'c mut Ctx,
text: &'t str,
}
impl<'c, 't: 'c> CollectNames<'c, 't> {
pub fn new(ctx: &'c mut Ctx<'t>) -> Self {
CollectNames { ctx }
default_visitor!(Expr for CollectNames<'_, '_> where Out = ());
empty_visitor!(FunCallExpr for CollectNames<'_, '_>);
empty_visitor!(FunExpr for CollectNames<'_, '_>);
impl<'c, 't> CollectNames<'c, 't> {
pub fn new(ctx: &'c mut Ctx, text: &'t str) -> Self {
CollectNames { ctx, text }
}
pub fn collect(&mut self, stmts: &Vec<Stmt>) {
// Collect all LHS assignments
for stmt in stmts.iter() {
self.visit_stmt(stmt);
}
}
fn visit_base_expr(&mut self, expr: &BaseExpr) {
match &expr.kind {
BaseExprKind::Ident => {
let name = expr.text_at(self.ctx.text()).to_string();
self.ctx.name_stack_mut().add(name);
if let Stmt::Assign(stmt) = stmt {
self.visit(stmt);
}
BaseExprKind::List(l) | BaseExprKind::Tuple(l) => {
l.iter().for_each(|expr| self.visit_expr(expr));
}
BaseExprKind::Object(o) => {
o.iter().for_each(|(key, value)| {
self.visit_expr(key);
self.visit_expr(value);
});
}
_ => { /* no-op */ }
}
}
}
impl Visit for CollectNames<'_, '_> {
impl Visit<AssignStmt> for CollectNames<'_, '_> {
type Out = ();
fn visit_expr(&mut self, expr: &Expr) -> Self::Out {
match expr {
Expr::Bin(b) => {
self.visit_expr(&b.lhs);
// don't descend into dot-expressions - these are not names, but symbols
if b.op != BinOp::Dot {
self.visit_expr(&b.rhs);
}
}
Expr::Un(u) => {
self.visit_expr(&u.expr);
}
Expr::FunCall(f) => {
self.visit_expr(&f.expr);
f.args.iter().for_each(|expr| self.visit_expr(expr));
}
Expr::Index(i) => {
self.visit_expr(&i.expr);
self.visit_expr(&i.index);
}
Expr::Fun(_) => { /* no-op */ }
Expr::Base(b) => self.visit_base_expr(b),
}
fn visit(&mut self, stmt: &AssignStmt) -> Self::Out {
self.visit(&stmt.lhs);
}
}
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out {
match stmt {
Stmt::Assign(AssignStmt { lhs, rhs, .. }) => {
self.visit_expr(lhs);
self.visit_expr(rhs);
}
Stmt::Expr(expr) => {
self.visit_expr(expr);
}
impl Visit<BinExpr> for CollectNames<'_, '_> {
type Out = ();
fn visit(&mut self, expr: &BinExpr) -> Self::Out {
self.visit(&expr.lhs);
}
}
impl Visit<UnExpr> for CollectNames<'_, '_> {
type Out = ();
fn visit(&mut self, expr: &UnExpr) -> Self::Out {
self.visit(&expr.expr)
}
}
impl Visit<IndexExpr> for CollectNames<'_, '_> {
type Out = ();
fn visit(&mut self, expr: &IndexExpr) -> Self::Out {
self.visit(&expr.expr);
}
}
impl Visit<BaseExpr> for CollectNames<'_, '_> {
type Out = ();
fn visit(&mut self, expr: &BaseExpr) -> Self::Out {
// This is a LHS standalone expr
if let BaseExpr { kind: BaseExprKind::Ident, .. } = expr {
let name = expr.text_at(self.text).to_string();
self.ctx.name_stack_mut().add(name);
}
}
}

View File

@@ -1,29 +1,91 @@
use crate::syn::ast::{Stmt, Expr};
use crate::syn::ast::prelude::*;
pub trait Visit {
pub trait Visit<A: Accept> {
type Out;
fn visit<A: Accept<Self>>(&mut self, acceptor: &A) -> Self::Out
where Self: Sized
{
acceptor.accept(self)
}
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out;
fn visit_expr(&mut self, expr: &Expr) -> Self::Out;
fn visit(&mut self, acceptor: &A) -> Self::Out;
}
pub trait Accept<V: Visit> {
fn accept(&self, visitor: &mut V) -> V::Out;
pub trait Accept {
fn accept<V: Visit<Self>>(&self, visitor: &mut V) -> V::Out
where
Self: Sized;
}
impl<V: Visit> Accept<V> for Stmt {
fn accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_stmt(self)
pub trait DefaultAccept<V: Visit<Self>>: Accept + Sized
{
fn default_accept(&self, visitor: &mut V) -> V::Out;
}
#[macro_export]
macro_rules! default_visitor {
($default:ident for $visitor:ty where Out = $out:ty) => {
impl crate::compile::visit::Visit<$default> for $visitor {
type Out = $out;
fn visit(&mut self, acceptor: &$default) -> Self::Out {
acceptor.default_accept(self)
}
}
};
}
#[macro_export]
macro_rules! empty_visitor {
($default:ident for $visitor:ty) => {
impl crate::compile::visit::Visit<$default> for $visitor {
type Out = ();
fn visit(&mut self, _: &$default) -> Self::Out {}
}
}
}
impl<V: Visit> Accept<V> for Expr {
fn accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_expr(self)
}
macro_rules! impl_accept {
($what:ident) => {
impl crate::compile::visit::Accept for $what {
fn accept<V: Visit<Self>>(&self, visitor: &mut V) -> V::Out {
visitor.visit(self)
}
}
};
}
macro_rules! impl_default_accept {
($what:ident : $($requires:ident),+ => $($tail:tt)+) => {
impl<V, Out> DefaultAccept<V> for $what
where V: Visit<Self, Out=Out> $(+ Visit<$requires, Out=Out>)+
{
$($tail)+
}
};
}
impl_accept!(Stmt);
impl_default_accept!(Stmt : AssignStmt, Expr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Stmt::Assign(a) => visitor.visit(a),
Stmt::Expr(e) => visitor.visit(e),
}
}
);
impl_accept!(AssignStmt);
impl_accept!(Expr);
impl_default_accept!(Expr: BinExpr, UnExpr, FunCallExpr, IndexExpr, FunExpr, BaseExpr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Expr::Bin(b) => visitor.visit(b.as_ref()),
Expr::Un(u) => visitor.visit(u.as_ref()),
Expr::FunCall(f) => visitor.visit(f.as_ref()),
Expr::Index(i) => visitor.visit(i.as_ref()),
Expr::Fun(f) => visitor.visit(f.as_ref()),
Expr::Base(b) => visitor.visit(b),
}
}
);
impl_accept!(BinExpr);
impl_accept!(UnExpr);
impl_accept!(FunCallExpr);
impl_accept!(IndexExpr);
impl_accept!(FunExpr);
impl_accept!(BaseExpr);