From 723b231b0d65e6f0bc8cecf3c1e5df4b143dea2b Mon Sep 17 00:00:00 2001 From: John Doty Date: Wed, 7 Feb 2024 02:57:53 -0800 Subject: [PATCH] [fine] Constant eval, use for loop type analysis --- fine/src/semantics.rs | 171 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 39227536..df9dba94 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -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 { 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 { + 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 { + // 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 { + // 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) { 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) {