comptime folding can now deal with declrefs
This commit is contained in:
parent
d4918a722f
commit
62cf214bde
249
src/ast.rs
249
src/ast.rs
|
@ -530,179 +530,135 @@ pub mod tree_visitor {
|
|||
children: Vec<Node>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum End {
|
||||
Open,
|
||||
Inclusive(Node),
|
||||
Exclusive(Node),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum PrePost {
|
||||
Pre(Node),
|
||||
Post(Node),
|
||||
}
|
||||
|
||||
impl PrePost {
|
||||
fn node(self) -> Node {
|
||||
match self {
|
||||
PrePost::Pre(n) => n,
|
||||
PrePost::Post(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Don't modify `node` in `pre()`
|
||||
/// Don't modify `children` in `pre()`
|
||||
pub struct Visitor<'a, F1, F2> {
|
||||
tree: &'a mut Tree,
|
||||
pub struct Visitor<F1, F2> {
|
||||
frames: Vec<Frame>,
|
||||
current_node: Option<Node>,
|
||||
end: End,
|
||||
pre: F1,
|
||||
post: F2,
|
||||
}
|
||||
|
||||
impl<'a, F1, F2> Visitor<'a, F1, F2> {
|
||||
pub fn new<T, U>(tree: &'a mut Tree, start: Node, pre: F1, post: F2) -> Visitor<'a, F1, F2>
|
||||
where
|
||||
F1: FnMut(&mut Tree, Node) -> T,
|
||||
F2: FnMut(&mut Tree, Node) -> U,
|
||||
{
|
||||
impl<F1, F2> Visitor<F1, F2> {
|
||||
pub fn new(start: Node, pre: F1, post: F2) -> Self {
|
||||
Self::new_inner(start, End::Open, pre, post)
|
||||
}
|
||||
pub fn new_range(start: Node, end: Node, pre: F1, post: F2) -> Self {
|
||||
Self::new_inner(start, End::Exclusive(end), pre, post)
|
||||
}
|
||||
pub fn new_range_inclusive(start: Node, end: Node, pre: F1, post: F2) -> Self {
|
||||
Self::new_inner(start, End::Inclusive(end), pre, post)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F1, F2> Visitor<F1, F2> {
|
||||
fn new_inner(start: Node, end: End, pre: F1, post: F2) -> Self {
|
||||
let frame = Frame {
|
||||
node: Node::MAX,
|
||||
children: vec![start],
|
||||
};
|
||||
Self {
|
||||
frames: vec![frame],
|
||||
tree,
|
||||
current_node: None,
|
||||
end,
|
||||
pre,
|
||||
post,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_children(&self, node: Node) -> Vec<Node> {
|
||||
match self.tree.nodes.get_node(node) {
|
||||
super::Tag::FunctionProto {
|
||||
name,
|
||||
parameters,
|
||||
return_type,
|
||||
} => {
|
||||
if let Some(params) = parameters {
|
||||
vec![*name, *params, *return_type]
|
||||
} else {
|
||||
vec![*name, *return_type]
|
||||
}
|
||||
}
|
||||
super::Tag::ParameterList { parameters } => parameters.clone(),
|
||||
super::Tag::Parameter { name, ty } => {
|
||||
vec![*name, *ty]
|
||||
}
|
||||
super::Tag::Pointer { pointee } => {
|
||||
vec![*pointee]
|
||||
}
|
||||
super::Tag::FunctionDecl { proto, body } => {
|
||||
vec![*proto, *body]
|
||||
}
|
||||
super::Tag::Block {
|
||||
statements,
|
||||
trailing_expr,
|
||||
} => {
|
||||
let mut children = statements.clone();
|
||||
if let Some(expr) = trailing_expr {
|
||||
children.push(*expr);
|
||||
}
|
||||
children
|
||||
}
|
||||
super::Tag::ReturnStmt { expr } => expr.into_iter().cloned().collect::<Vec<_>>(),
|
||||
&super::Tag::ExprStmt { expr } => {
|
||||
vec![expr]
|
||||
}
|
||||
super::Tag::VarDecl {
|
||||
name,
|
||||
explicit_type,
|
||||
..
|
||||
} => {
|
||||
if let Some(ty) = *explicit_type {
|
||||
vec![*name, ty]
|
||||
} else {
|
||||
vec![*name]
|
||||
}
|
||||
}
|
||||
super::Tag::GlobalDecl {
|
||||
name,
|
||||
explicit_type,
|
||||
..
|
||||
} => {
|
||||
if let Some(ty) = *explicit_type {
|
||||
vec![*name, ty]
|
||||
} else {
|
||||
vec![*name]
|
||||
}
|
||||
}
|
||||
&super::Tag::CallExpr { lhs, rhs } => {
|
||||
if let Some(rhs) = rhs {
|
||||
vec![lhs, rhs]
|
||||
} else {
|
||||
vec![lhs]
|
||||
}
|
||||
}
|
||||
super::Tag::ArgumentList { parameters } => parameters.clone(),
|
||||
&super::Tag::Argument { name, expr } => {
|
||||
if let Some(name) = name {
|
||||
vec![name, expr]
|
||||
} else {
|
||||
vec![expr]
|
||||
}
|
||||
}
|
||||
&super::Tag::ExplicitCast { lhs, typename } => {
|
||||
vec![lhs, typename]
|
||||
}
|
||||
super::Tag::Deref { lhs }
|
||||
| super::Tag::Ref { lhs }
|
||||
| super::Tag::Not { lhs }
|
||||
| super::Tag::Negate { lhs } => {
|
||||
vec![*lhs]
|
||||
}
|
||||
super::Tag::Or { lhs, rhs }
|
||||
| super::Tag::And { lhs, rhs }
|
||||
| super::Tag::BitOr { lhs, rhs }
|
||||
| super::Tag::BitAnd { lhs, rhs }
|
||||
| super::Tag::BitXOr { lhs, rhs }
|
||||
| super::Tag::Eq { lhs, rhs }
|
||||
| super::Tag::NEq { lhs, rhs }
|
||||
| super::Tag::Lt { lhs, rhs }
|
||||
| super::Tag::Gt { lhs, rhs }
|
||||
| super::Tag::Le { lhs, rhs }
|
||||
| super::Tag::Ge { lhs, rhs }
|
||||
| super::Tag::Shl { lhs, rhs }
|
||||
| super::Tag::Shr { lhs, rhs }
|
||||
| super::Tag::Add { lhs, rhs }
|
||||
| super::Tag::Sub { lhs, rhs }
|
||||
| super::Tag::Mul { lhs, rhs }
|
||||
| super::Tag::Rem { lhs, rhs }
|
||||
| super::Tag::Div { lhs, rhs }
|
||||
| super::Tag::Assign { lhs, rhs } => {
|
||||
vec![*lhs, *rhs]
|
||||
}
|
||||
_ => vec![],
|
||||
fn next_node(&mut self, tree: &Tree) -> Option<PrePost> {
|
||||
if let Some(node) = self.current_node.take() {
|
||||
let mut children = tree.get_node_children(node);
|
||||
children.reverse();
|
||||
self.frames.push(Frame { node, children });
|
||||
}
|
||||
}
|
||||
|
||||
fn next_node(&mut self) -> Option<PrePost> {
|
||||
loop {
|
||||
let node = {
|
||||
let frame = self.frames.last_mut()?;
|
||||
if let Some(node) = frame.children.pop() {
|
||||
return Some(PrePost::Pre(node));
|
||||
if self.end == End::Exclusive(node) {
|
||||
self.frames.clear();
|
||||
None
|
||||
} else {
|
||||
self.current_node = Some(node);
|
||||
Some(PrePost::Pre(node))
|
||||
}
|
||||
} else {
|
||||
let frame = self.frames.pop()?;
|
||||
if frame.node != Node::MAX {
|
||||
return Some(PrePost::Post(frame.node));
|
||||
let node = frame.node;
|
||||
|
||||
if node == Node::MAX {
|
||||
self.frames.clear();
|
||||
None
|
||||
} else {
|
||||
if self.end == End::Inclusive(node) {
|
||||
self.frames.clear();
|
||||
}
|
||||
Some(PrePost::Post(node))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
pub fn skip_until(mut self, tree: &Tree, node: Node) -> Self {
|
||||
self.find(tree, node);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn find(&mut self, tree: &Tree, needle: Node) {
|
||||
loop {
|
||||
let Some(node) = self.next_node(tree) else {
|
||||
break;
|
||||
};
|
||||
if node == PrePost::Pre(needle) {
|
||||
self.frames.last_mut().unwrap().children.push(needle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_ok<T, E>(mut self) -> core::result::Result<T, E>
|
||||
/// short-circuits on the first E
|
||||
pub fn visit_ok<T, E>(mut self, tree: &Tree) -> core::result::Result<T, E>
|
||||
where
|
||||
F1: FnMut(&mut Tree, Node) -> core::result::Result<T, E>,
|
||||
F2: FnMut(&mut Tree, Node) -> core::result::Result<T, E>,
|
||||
F1: FnMut(&Tree, Node) -> core::result::Result<T, E>,
|
||||
F2: FnMut(&Tree, Node) -> core::result::Result<T, E>,
|
||||
{
|
||||
let mut t = None;
|
||||
loop {
|
||||
let Some(node) = self.next_node() else {
|
||||
let Some(node) = self.next_node(tree) else {
|
||||
break;
|
||||
};
|
||||
|
||||
match node {
|
||||
PrePost::Pre(node) => {
|
||||
t = Some((self.pre)(self.tree, node)?);
|
||||
let children = self.get_children(node);
|
||||
self.frames.push(Frame { node, children });
|
||||
t = Some((self.pre)(tree, node)?);
|
||||
}
|
||||
PrePost::Post(node) => {
|
||||
t = Some((self.post)(self.tree, node)?);
|
||||
t = Some((self.post)(tree, node)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -710,24 +666,43 @@ pub mod tree_visitor {
|
|||
Ok(t.unwrap())
|
||||
}
|
||||
|
||||
pub fn visit<T, U>(mut self)
|
||||
pub fn visit<T, U>(mut self, tree: &Tree)
|
||||
where
|
||||
F1: FnMut(&mut Tree, Node) -> T,
|
||||
F2: FnMut(&mut Tree, Node) -> U,
|
||||
F1: FnMut(&Tree, Node) -> T,
|
||||
F2: FnMut(&Tree, Node) -> U,
|
||||
{
|
||||
loop {
|
||||
let Some(node) = self.next_node() else {
|
||||
let Some(node) = self.next_node(tree) else {
|
||||
break;
|
||||
};
|
||||
|
||||
match node {
|
||||
PrePost::Pre(node) => {
|
||||
(self.pre)(self.tree, node);
|
||||
let children = self.get_children(node);
|
||||
self.frames.push(Frame { node, children });
|
||||
(self.pre)(tree, node);
|
||||
}
|
||||
PrePost::Post(node) => {
|
||||
(self.post)(self.tree, node);
|
||||
(self.post)(tree, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_mut<T, U>(mut self, tree: &mut Tree)
|
||||
where
|
||||
F1: FnMut(&mut Tree, Node) -> T,
|
||||
F2: FnMut(&mut Tree, Node) -> U,
|
||||
{
|
||||
loop {
|
||||
let Some(node) = self.next_node(tree) else {
|
||||
break;
|
||||
};
|
||||
|
||||
match node {
|
||||
PrePost::Pre(node) => {
|
||||
(self.pre)(tree, node);
|
||||
}
|
||||
PrePost::Post(node) => {
|
||||
(self.post)(tree, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
203
src/parser.rs
203
src/parser.rs
|
@ -12,6 +12,7 @@ use crate::{
|
|||
string_table::{ImmOrIndex, Index, StringTable},
|
||||
symbol_table::{SymbolKind, SymbolTable},
|
||||
tokens::Token,
|
||||
variant,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -81,6 +82,10 @@ impl Nodes {
|
|||
fn reserve_node(&mut self) -> Node {
|
||||
self.push_tag(Tag::Undefined)
|
||||
}
|
||||
|
||||
fn swap_nodes(&mut self, lhs: Node, rhs: Node) {
|
||||
self.inner.swap(lhs.get() as usize, rhs.get() as usize);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add a string-table which stores strings and maybe other bytes and
|
||||
|
@ -973,6 +978,113 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
pub fn get_node_children(&self, node: Node) -> Vec<Node> {
|
||||
match self.nodes.get_node(node) {
|
||||
Tag::FunctionProto {
|
||||
name,
|
||||
parameters,
|
||||
return_type,
|
||||
} => {
|
||||
if let Some(params) = parameters {
|
||||
vec![*name, *params, *return_type]
|
||||
} else {
|
||||
vec![*name, *return_type]
|
||||
}
|
||||
}
|
||||
Tag::ParameterList { parameters } => parameters.clone(),
|
||||
Tag::Parameter { name, ty } => {
|
||||
vec![*name, *ty]
|
||||
}
|
||||
Tag::Pointer { pointee } => {
|
||||
vec![*pointee]
|
||||
}
|
||||
Tag::FunctionDecl { proto, body } => {
|
||||
vec![*proto, *body]
|
||||
}
|
||||
Tag::Block {
|
||||
statements,
|
||||
trailing_expr,
|
||||
} => {
|
||||
let mut children = statements.clone();
|
||||
if let Some(expr) = trailing_expr {
|
||||
children.push(*expr);
|
||||
}
|
||||
children
|
||||
}
|
||||
Tag::ReturnStmt { expr } => expr.into_iter().cloned().collect::<Vec<_>>(),
|
||||
&Tag::ExprStmt { expr } => {
|
||||
vec![expr]
|
||||
}
|
||||
Tag::VarDecl {
|
||||
name,
|
||||
explicit_type,
|
||||
..
|
||||
} => {
|
||||
if let Some(ty) = *explicit_type {
|
||||
vec![*name, ty]
|
||||
} else {
|
||||
vec![*name]
|
||||
}
|
||||
}
|
||||
Tag::GlobalDecl {
|
||||
name,
|
||||
explicit_type,
|
||||
..
|
||||
} => {
|
||||
if let Some(ty) = *explicit_type {
|
||||
vec![*name, ty]
|
||||
} else {
|
||||
vec![*name]
|
||||
}
|
||||
}
|
||||
&Tag::CallExpr { lhs, rhs } => {
|
||||
if let Some(rhs) = rhs {
|
||||
vec![lhs, rhs]
|
||||
} else {
|
||||
vec![lhs]
|
||||
}
|
||||
}
|
||||
Tag::ArgumentList { parameters } => parameters.clone(),
|
||||
&Tag::Argument { name, expr } => {
|
||||
if let Some(name) = name {
|
||||
vec![name, expr]
|
||||
} else {
|
||||
vec![expr]
|
||||
}
|
||||
}
|
||||
&Tag::ExplicitCast { lhs, typename } => {
|
||||
vec![lhs, typename]
|
||||
}
|
||||
Tag::Deref { lhs } | Tag::Ref { lhs } | Tag::Not { lhs } | Tag::Negate { lhs } => {
|
||||
vec![*lhs]
|
||||
}
|
||||
Tag::Or { lhs, rhs }
|
||||
| Tag::And { lhs, rhs }
|
||||
| Tag::BitOr { lhs, rhs }
|
||||
| Tag::BitAnd { lhs, rhs }
|
||||
| Tag::BitXOr { lhs, rhs }
|
||||
| Tag::Eq { lhs, rhs }
|
||||
| Tag::NEq { lhs, rhs }
|
||||
| Tag::Lt { lhs, rhs }
|
||||
| Tag::Gt { lhs, rhs }
|
||||
| Tag::Le { lhs, rhs }
|
||||
| Tag::Ge { lhs, rhs }
|
||||
| Tag::Shl { lhs, rhs }
|
||||
| Tag::Shr { lhs, rhs }
|
||||
| Tag::Add { lhs, rhs }
|
||||
| Tag::Sub { lhs, rhs }
|
||||
| Tag::Mul { lhs, rhs }
|
||||
| Tag::Rem { lhs, rhs }
|
||||
| Tag::Div { lhs, rhs }
|
||||
| Tag::Assign { lhs, rhs } => {
|
||||
vec![*lhs, *rhs]
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
fn render_node<W: core::fmt::Write>(
|
||||
&mut self,
|
||||
|
@ -1605,15 +1717,62 @@ impl Tree {
|
|||
|
||||
fn fold_comptime_with_visitor(&mut self, decl: Node) {
|
||||
ast::tree_visitor::Visitor::new(
|
||||
self,
|
||||
decl,
|
||||
|_, node| {
|
||||
eprint!("%{node} ");
|
||||
},
|
||||
|tree, node| {
|
||||
if let Ok(value) = tree.fold_comptime_inner(node) {
|
||||
|_: &mut Tree, _| {},
|
||||
|tree: &mut Tree, node| {
|
||||
|
||||
let value_node = if let &Tag::DeclRef(lhs) = tree.nodes.get_node(node) {
|
||||
let start = lhs;
|
||||
let end = node;
|
||||
let mut is_comptime = true;
|
||||
let mut last_value = None;
|
||||
eprintln!(
|
||||
"checking if %{}, referencing %{} is comptime-evaluable",
|
||||
node.get(),
|
||||
lhs.get()
|
||||
);
|
||||
ast::tree_visitor::Visitor::new_range_inclusive(
|
||||
decl,
|
||||
end,
|
||||
|_: &Tree, _| {
|
||||
},
|
||||
|tree: &Tree, node| match tree.nodes.get_node(node) {
|
||||
&Tag::Assign { lhs, rhs } => {
|
||||
if lhs == start || matches!(tree.nodes.get_node(lhs), &Tag::DeclRef(decl) if decl == start) {
|
||||
eprintln!("found assignment at %{}", node.get());
|
||||
is_comptime &= tree.is_node_comptime(rhs);
|
||||
if is_comptime {
|
||||
last_value = Some(rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
&Tag::Ref { lhs } if lhs == start => {
|
||||
// recursively checking for derefs would get very complicated.
|
||||
is_comptime = false;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
)
|
||||
.skip_until(tree, start)
|
||||
.visit(tree);
|
||||
|
||||
eprintln!(
|
||||
"%{} is {}comptime-evaluable.",
|
||||
node.get(),
|
||||
if is_comptime { "" } else { "not " }
|
||||
);
|
||||
|
||||
eprintln!("%{node} comptime-value is %{last_value:?}");
|
||||
|
||||
is_comptime.then_some(last_value).flatten().unwrap_or(node)
|
||||
}else {
|
||||
node
|
||||
};
|
||||
|
||||
if let Ok(value) = tree.fold_comptime_inner(value_node) {
|
||||
let (bytes, ty) = value.into_bytes_and_type();
|
||||
let idx = tree.strings.insert(bytes);
|
||||
|
||||
*tree.nodes.get_node_mut(node) = Tag::Constant {
|
||||
bytes: ImmOrIndex::Index(idx),
|
||||
ty,
|
||||
|
@ -1621,11 +1780,10 @@ impl Tree {
|
|||
}
|
||||
},
|
||||
)
|
||||
.visit();
|
||||
.visit_mut(self);
|
||||
}
|
||||
|
||||
fn fold_comptime_inner(&mut self, decl: Node) -> comptime::Result<ComptimeNumber> {
|
||||
//
|
||||
if self.is_node_comptime(decl) {
|
||||
match self.nodes.get_node(decl) {
|
||||
Tag::Constant { bytes, ty } => {
|
||||
|
@ -1679,9 +1837,6 @@ impl Tree {
|
|||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
Tag::DeclRef(lhs) => {
|
||||
return self.fold_comptime_inner(*lhs);
|
||||
}
|
||||
Tag::Not { lhs } => {
|
||||
let lhs = self.fold_comptime_inner(*lhs)?;
|
||||
return lhs.not();
|
||||
|
@ -1812,6 +1967,28 @@ impl Tree {
|
|||
|
||||
return lhs.div(rhs);
|
||||
}
|
||||
&Tag::DeclRef(lhs) => {
|
||||
variant!(self.nodes.get_node(lhs) => &Tag::VarDecl { assignment, .. });
|
||||
|
||||
let start = assignment.unwrap_or(lhs);
|
||||
let end = decl;
|
||||
let mut last_value = None;
|
||||
ast::tree_visitor::Visitor::new_range(
|
||||
start,
|
||||
end,
|
||||
|_: &Tree, _| {},
|
||||
|tree: &Tree, node| match tree.nodes.get_node(node) {
|
||||
&Tag::Assign { lhs, rhs } if lhs == start => {
|
||||
last_value = Some(rhs);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
)
|
||||
.visit(self);
|
||||
|
||||
return self
|
||||
.fold_comptime_inner(last_value.ok_or(comptime::Error::NotComptime)?);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -2061,7 +2238,9 @@ const global: u32 = 42u32;
|
|||
fn comptime() {
|
||||
let src = "
|
||||
fn main() -> void {
|
||||
let a = 3 * 49573 << 4;
|
||||
let x: u32;
|
||||
x = 666u32;
|
||||
let a = x + 3 * 49573 << 4;
|
||||
}
|
||||
";
|
||||
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
||||
|
|
Loading…
Reference in a new issue