[fine] Constant eval, use for loop type analysis

This commit is contained in:
John Doty 2024-02-07 02:57:53 -08:00
parent 239e859eaf
commit 723b231b0d

View file

@ -1,10 +1,12 @@
use crate::{
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
tokens::{Lines, Token, TokenKind},
vm::StackValue,
};
use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};
// TODO: Unused variables?
// TODO: Underscore for discard?
// TODO: An error should have:
//
@ -481,6 +483,29 @@ fn set_logical_parents(
}
}
// Process escapes and convert a string constant in source to a runtime String value.
pub fn string_constant_to_string(s: &str) -> String {
let mut result = String::new();
let mut input = s.chars();
while let Some(ch) = input.next() {
if ch == '\\' {
if let Some(ch) = input.next() {
match ch {
'n' => result.push('\n'),
'r' => result.push('\r'),
't' => result.push('\t'),
_ => result.push(ch),
}
} else {
result.push(ch)
}
} else {
result.push(ch)
}
}
result
}
#[derive(Clone, Copy, Debug)]
enum Incremental<T> {
None,
@ -1137,6 +1162,7 @@ impl<'a> Semantics<'a> {
TreeKind::MemberAccess => self.type_of_member_access(t, tree),
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
TreeKind::Parameter => self.type_of_parameter(tree),
TreeKind::Pattern => self.type_of_pattern(tree),
TreeKind::ReturnStatement => Some(Type::Unreachable),
TreeKind::ReturnType => self.type_of_return_type(tree),
TreeKind::SelfParameter => self.type_of_self_parameter(tree),
@ -1145,7 +1171,7 @@ impl<'a> Semantics<'a> {
TreeKind::TypeIdentifier => self.type_of_type_identifier(t, tree),
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
TreeKind::UnaryExpression => self.type_of_unary(tree),
TreeKind::WhileStatement => Some(Type::Nothing),
TreeKind::WhileStatement => self.type_of_while(tree),
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
};
@ -1960,6 +1986,147 @@ impl<'a> Semantics<'a> {
Some(self.type_of(tree.nth_tree(2)?))
}
fn type_of_while(&self, tree: &Tree) -> Option<Type> {
match self.constant_eval(tree.nth_tree(1)?) {
Some(StackValue::Bool(true)) => Some(Type::Unreachable),
_ => Some(Type::Nothing),
}
}
fn type_of_pattern(&self, tree: &Tree) -> Option<Type> {
// We know that we have a type expression in here, that's what we're asking about.
Some(self.type_of(tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?))
}
// TODO: Really want to TEST THIS also uh can we generate bytecode for functions and call it??
fn constant_eval(&self, t: TreeRef) -> Option<StackValue> {
// TODO: Make this cached, incremental, so the compiler can use it for optimizations.
let tree = &self.syntax_tree[t];
match tree.kind {
TreeKind::LiteralExpression => {
let tok = tree.nth_token(0)?;
match self.type_of(t) {
Type::F64 => Some(StackValue::Float(tok.as_str().parse().unwrap())),
Type::Bool => Some(StackValue::Bool(tok.kind == TokenKind::True)),
Type::String => Some(StackValue::String(
string_constant_to_string(tok.as_str()).into(),
)),
Type::Nothing => Some(StackValue::Nothing), // ?
_ => None,
}
}
TreeKind::IsExpression => {
let pt = tree.nth_tree(2)?;
let pattern = &self.syntax_tree[pt];
if pattern
.child_of_kind(self.syntax_tree, TreeKind::WildcardPattern)
.is_some()
{
Some(StackValue::Bool(true))
} else if self.can_convert(&self.type_of(tree.nth_tree(0)?), &self.type_of(pt)) {
Some(StackValue::Bool(true))
} else {
None
}
}
TreeKind::GroupingExpression => self.constant_eval(tree.nth_tree(1)?),
TreeKind::UnaryExpression => {
let op = tree.nth_token(0)?.kind;
let val = self.constant_eval(tree.nth_tree(1)?)?;
match (op, val) {
(TokenKind::Plus, StackValue::Float(a)) => Some(StackValue::Float(a)),
(TokenKind::Minus, StackValue::Float(a)) => Some(StackValue::Float(-a)),
(TokenKind::Bang, StackValue::Bool(a)) => Some(StackValue::Bool(!a)),
_ => None,
}
}
TreeKind::BinaryExpression => {
let left = self.constant_eval(tree.nth_tree(0)?)?;
let right = self.constant_eval(tree.nth_tree(2)?)?;
let op = tree.nth_token(1)?.kind;
match (op, left, right) {
(TokenKind::Plus, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a + b))
}
(TokenKind::Minus, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a - b))
}
(TokenKind::Star, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Float(a * b))
}
(TokenKind::Slash, StackValue::Float(a), StackValue::Float(b)) => {
if b != 0.0 {
Some(StackValue::Float(a / b))
} else {
None // TODO: Error
}
}
(TokenKind::Plus, StackValue::String(a), StackValue::String(b)) => {
let mut result = String::new();
result.push_str(&*a);
result.push_str(&*b);
Some(StackValue::String(result.into()))
}
(TokenKind::And, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a && b))
}
(TokenKind::Or, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a || b))
}
(TokenKind::EqualEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::Bool(a), StackValue::Bool(b)) => {
Some(StackValue::Bool(a == b))
}
(TokenKind::EqualEqual, StackValue::Nothing, StackValue::Nothing) => {
Some(StackValue::Bool(true))
}
(TokenKind::Less, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a < b))
}
(TokenKind::LessEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a <= b))
}
(TokenKind::Greater, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a > b))
}
(TokenKind::GreaterEqual, StackValue::Float(a), StackValue::Float(b)) => {
Some(StackValue::Bool(a >= b))
}
(TokenKind::Less, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a < b))
}
(TokenKind::LessEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a <= b))
}
(TokenKind::Greater, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a > b))
}
(TokenKind::GreaterEqual, StackValue::String(a), StackValue::String(b)) => {
Some(StackValue::Bool(a >= b))
}
_ => None,
}
}
_ => None,
}
}
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
eprintln!("Parsed the tree as:");
eprintln!("\n{}", self.syntax_tree.dump(true));
@ -2289,6 +2456,8 @@ fn check_pattern(s: &Semantics, tree: &Tree) {
}
}
}
// TODO: Warn on constant match
}
fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {