[fine] Constant eval, use for loop type analysis
This commit is contained in:
parent
239e859eaf
commit
723b231b0d
1 changed files with 170 additions and 1 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
|
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
|
||||||
tokens::{Lines, Token, TokenKind},
|
tokens::{Lines, Token, TokenKind},
|
||||||
|
vm::StackValue,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};
|
||||||
|
|
||||||
// TODO: Unused variables?
|
// TODO: Unused variables?
|
||||||
|
// TODO: Underscore for discard?
|
||||||
|
|
||||||
// TODO: An error should have:
|
// 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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum Incremental<T> {
|
enum Incremental<T> {
|
||||||
None,
|
None,
|
||||||
|
|
@ -1137,6 +1162,7 @@ impl<'a> Semantics<'a> {
|
||||||
TreeKind::MemberAccess => self.type_of_member_access(t, tree),
|
TreeKind::MemberAccess => self.type_of_member_access(t, tree),
|
||||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
||||||
TreeKind::Parameter => self.type_of_parameter(tree),
|
TreeKind::Parameter => self.type_of_parameter(tree),
|
||||||
|
TreeKind::Pattern => self.type_of_pattern(tree),
|
||||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||||
TreeKind::ReturnType => self.type_of_return_type(tree),
|
TreeKind::ReturnType => self.type_of_return_type(tree),
|
||||||
TreeKind::SelfParameter => self.type_of_self_parameter(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::TypeIdentifier => self.type_of_type_identifier(t, tree),
|
||||||
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
|
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
|
||||||
TreeKind::UnaryExpression => self.type_of_unary(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"),
|
_ => 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)?))
|
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>) {
|
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
|
||||||
eprintln!("Parsed the tree as:");
|
eprintln!("Parsed the tree as:");
|
||||||
eprintln!("\n{}", self.syntax_tree.dump(true));
|
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) {
|
fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue