Compare commits

..

No commits in common. "3415b1a3f6b252fcdb6a2ef9235e13eddaf1da04" and "1cc5ce6ca9df93d5206e175140726d45254cf0c5" have entirely different histories.

9 changed files with 47 additions and 183 deletions

View file

@ -15,23 +15,18 @@ pub enum Instruction {
BoolNot, BoolNot,
Call(usize), Call(usize),
CompareBool,
CompareFloat,
CompareString,
Discard, Discard,
Dup, Dup,
EqBool,
EqFloat,
EqString,
FloatAdd, FloatAdd,
FloatDivide, FloatDivide,
FloatMultiply, FloatMultiply,
FloatSubtract, FloatSubtract,
GreaterFloat,
GreaterString,
IsClass(i64),
Jump(usize), Jump(usize),
JumpFalse(usize), JumpFalse(usize),
JumpTrue(usize), // TODO: Only one of these, and use BoolNot? JumpTrue(usize), // TODO: Only one of these, and use BoolNot?
LessFloat,
LessString,
LoadArgument(usize), LoadArgument(usize),
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index. LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
LoadFunction(usize), LoadFunction(usize),
@ -41,7 +36,6 @@ pub enum Instruction {
NewObject(usize), NewObject(usize),
PushFalse, PushFalse,
PushFloat(f64), PushFloat(f64),
PushInt(i64),
PushNothing, PushNothing,
PushString(usize), PushString(usize),
PushTrue, PushTrue,
@ -50,6 +44,9 @@ pub enum Instruction {
StoreLocal(usize), StoreLocal(usize),
StoreModule(usize), StoreModule(usize),
StringAdd, StringAdd,
IsClass(i64),
PushInt(i64),
} }
pub enum Export { pub enum Export {
@ -414,8 +411,7 @@ where
} }
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
let op = tr.nth_token(1)?; match tr.nth_token(1)?.kind {
match op.kind {
TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t { TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::FloatAdd, Type::F64 => Instruction::FloatAdd,
Type::String => Instruction::StringAdd, Type::String => Instruction::StringAdd,
@ -430,36 +426,6 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
TokenKind::Slash => { TokenKind::Slash => {
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide) compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide)
} }
TokenKind::Less => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString,
_ => inst_panic!("panic less {}", t),
}),
TokenKind::LessEqual => {
compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::GreaterFloat,
Type::String => Instruction::GreaterString,
_ => inst_panic!("panic less equal {}", t),
});
c.push(Instruction::BoolNot);
OK
}
TokenKind::Greater => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::GreaterFloat,
Type::String => Instruction::GreaterString,
_ => inst_panic!("panic greater {}", t),
}),
TokenKind::GreaterEqual => {
compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString,
_ => inst_panic!("panic greater equal {}", t),
});
c.push(Instruction::BoolNot);
OK
}
TokenKind::And => { TokenKind::And => {
// Compile the left hand side, it leaves a bool on the stack // Compile the left hand side, it leaves a bool on the stack
compile_expression(c, tr.nth_tree(0)?); compile_expression(c, tr.nth_tree(0)?);
@ -520,9 +486,9 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Instruction::PushTrue Instruction::PushTrue
} else { } else {
match arg_type { match arg_type {
Type::F64 => Instruction::EqFloat, Type::F64 => Instruction::CompareFloat,
Type::String => Instruction::EqString, Type::String => Instruction::CompareString,
Type::Bool => Instruction::EqBool, // ? Type::Bool => Instruction::CompareBool, // ?
_ => inst_panic!("panic comparing {}", arg_type), _ => inst_panic!("panic comparing {}", arg_type),
} }
} }
@ -579,7 +545,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
} }
OK OK
} }
_ => ice!(c, t, "Unsupported binary expression '{op}'"), _ => ice!(c, t, "Unsupported binary expression"),
} }
} }
@ -932,15 +898,13 @@ fn compile_self_reference(c: &mut Compiler) -> CR {
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
let tree = &c.semantics.tree()[t]; let tree = &c.semantics.tree()[t];
let cr = match tree.kind { let cr = match tree.kind {
TreeKind::Block => compile_block_statement(c, t, gen_value),
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value), TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value), TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
TreeKind::WhileStatement => compile_while_statement(c, tree, gen_value), TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
_ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind), TreeKind::Block => compile_block_statement(c, t, gen_value),
_ => ice!(c, t, "unsupported tree kind {:?}", tree.kind),
}; };
if matches!(cr, None) { if matches!(cr, None) {
c.push(inst_panic!("stat {:?}", tree)); c.push(inst_panic!("stat {:?}", tree));
@ -1112,20 +1076,3 @@ fn compile_block_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) -> CR
OK OK
} }
fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
let start_index = c.function.instructions.len();
compile_expression(c, tree.nth_tree(1)?);
let jump_end_index = c.push(Instruction::JumpFalse(0));
compile_block_statement(c, tree.nth_tree(2)?, false);
c.push(Instruction::Jump(start_index));
c.patch(jump_end_index, |i| Instruction::JumpFalse(i));
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}

View file

@ -143,14 +143,10 @@ pub enum TreeKind {
ListConstructor, ListConstructor,
ListConstructorElement, ListConstructorElement,
LiteralExpression, LiteralExpression,
MatchArm,
MatchBody,
MatchExpression,
MemberAccess, MemberAccess,
NewObjectExpression, NewObjectExpression,
ParamList, ParamList,
Parameter, Parameter,
Pattern,
ReturnStatement, ReturnStatement,
ReturnType, ReturnType,
SelfParameter, SelfParameter,
@ -160,8 +156,13 @@ pub enum TreeKind {
TypeParameter, TypeParameter,
TypeParameterList, TypeParameterList,
UnaryExpression, UnaryExpression,
Pattern,
VariableBinding, VariableBinding,
WhileStatement,
MatchExpression,
MatchBody,
MatchArm,
WildcardPattern, WildcardPattern,
} }
@ -780,7 +781,6 @@ const STATEMENT_RECOVERY: &[TokenKind] = &[
TokenKind::Return, TokenKind::Return,
TokenKind::For, TokenKind::For,
TokenKind::Class, TokenKind::Class,
TokenKind::While,
]; ];
fn block(p: &mut CParser) { fn block(p: &mut CParser) {
@ -813,8 +813,6 @@ fn statement(p: &mut CParser) -> bool {
// require a semicolon at the end if it's all by itself. // require a semicolon at the end if it's all by itself.
TokenKind::If => statement_if(p), TokenKind::If => statement_if(p),
TokenKind::While => statement_while(p),
_ => { _ => {
if p.at(TokenKind::Semicolon) || p.at_any(EXPRESSION_FIRST) { if p.at(TokenKind::Semicolon) || p.at_any(EXPRESSION_FIRST) {
statement_expression(p) statement_expression(p)
@ -836,24 +834,6 @@ fn statement_if(p: &mut CParser) {
p.end(m, TreeKind::IfStatement); p.end(m, TreeKind::IfStatement);
} }
fn statement_while(p: &mut CParser) {
let m = p.start();
p.expect_start(TokenKind::While);
if p.at_any(EXPRESSION_FIRST) {
expression(p);
} else {
p.error("expected an expression for the loop condition");
}
if p.at(TokenKind::LeftBrace) {
block(p);
} else {
p.error("expected a block for the loop body");
}
p.end(m, TreeKind::WhileStatement);
}
fn statement_let(p: &mut CParser) { fn statement_let(p: &mut CParser) {
let m = p.start(); let m = p.start();

View file

@ -171,7 +171,7 @@ impl fmt::Display for Type {
Error => write!(f, "<< INTERNAL ERROR >>"), Error => write!(f, "<< INTERNAL ERROR >>"),
Unreachable => write!(f, "<< UNREACHABLE >>"), Unreachable => write!(f, "<< UNREACHABLE >>"),
Assignment(_) => write!(f, "assignment"), Assignment(_) => write!(f, "assignment"),
Nothing => write!(f, "nothing"), Nothing => write!(f, "()"),
F64 => write!(f, "f64"), F64 => write!(f, "f64"),
I64 => write!(f, "i64"), I64 => write!(f, "i64"),
String => write!(f, "string"), String => write!(f, "string"),
@ -1139,9 +1139,9 @@ impl<'a> Semantics<'a> {
TreeKind::Block => self.type_of_block(tree), TreeKind::Block => self.type_of_block(tree),
TreeKind::CallExpression => self.type_of_call(tree), TreeKind::CallExpression => self.type_of_call(tree),
TreeKind::ClassDecl => self.type_of_class_decl(t, tree), TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
TreeKind::FieldDecl => self.type_of_field_decl(tree),
TreeKind::ConditionalExpression => self.type_of_conditional(tree), TreeKind::ConditionalExpression => self.type_of_conditional(tree),
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree), TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
TreeKind::FieldDecl => self.type_of_field_decl(tree),
TreeKind::FieldValue => self.type_of_field_value(t, tree), TreeKind::FieldValue => self.type_of_field_value(t, tree),
TreeKind::ForStatement => Some(Type::Nothing), TreeKind::ForStatement => Some(Type::Nothing),
TreeKind::FunctionDecl => self.type_of_function_decl(tree), TreeKind::FunctionDecl => self.type_of_function_decl(tree),
@ -1153,9 +1153,6 @@ impl<'a> Semantics<'a> {
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree), TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree), TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
TreeKind::LiteralExpression => self.type_of_literal(tree), TreeKind::LiteralExpression => self.type_of_literal(tree),
TreeKind::MatchArm => self.type_of_match_arm(tree),
TreeKind::MatchBody => self.type_of_match_body(tree),
TreeKind::MatchExpression => self.type_of_match_expression(tree),
TreeKind::MemberAccess => self.type_of_member_access(tree), TreeKind::MemberAccess => self.type_of_member_access(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),
@ -1167,7 +1164,10 @@ 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::MatchExpression => self.type_of_match_expression(tree),
TreeKind::MatchBody => self.type_of_match_body(tree),
TreeKind::MatchArm => self.type_of_match_arm(tree),
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
}; };
@ -1241,16 +1241,6 @@ impl<'a> Semantics<'a> {
(TokenKind::EqualEqual, Type::Bool, Type::Bool) => Some(Type::Bool), (TokenKind::EqualEqual, Type::Bool, Type::Bool) => Some(Type::Bool),
(TokenKind::EqualEqual, Type::Nothing, Type::Nothing) => Some(Type::Bool), (TokenKind::EqualEqual, Type::Nothing, Type::Nothing) => Some(Type::Bool),
(TokenKind::Less, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::LessEqual, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::Greater, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::GreaterEqual, Type::F64, Type::F64) => Some(Type::Bool),
(TokenKind::Less, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::LessEqual, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::Greater, Type::String, Type::String) => Some(Type::Bool),
(TokenKind::GreaterEqual, Type::String, Type::String) => Some(Type::Bool),
// This is dumb and should be punished, probably. // This is dumb and should be punished, probably.
(_, _, Type::Unreachable) => { (_, _, Type::Unreachable) => {
self.report_error( self.report_error(
@ -1363,7 +1353,7 @@ impl<'a> Semantics<'a> {
"f64" => Some(Type::F64), "f64" => Some(Type::F64),
"string" => Some(Type::String), "string" => Some(Type::String),
"bool" => Some(Type::Bool), "bool" => Some(Type::Bool),
"nothing" => Some(Type::Nothing), "()" => Some(Type::Nothing),
"list" => { "list" => {
let args = let args =
tree.child_tree_of_kind(self.syntax_tree, TreeKind::TypeParameterList)?; tree.child_tree_of_kind(self.syntax_tree, TreeKind::TypeParameterList)?;
@ -2051,8 +2041,6 @@ pub fn check(s: &Semantics) {
TreeKind::MatchArm => {} TreeKind::MatchArm => {}
TreeKind::MatchBody => check_match_body(s, t, tree), TreeKind::MatchBody => check_match_body(s, t, tree),
TreeKind::MatchExpression => {} TreeKind::MatchExpression => {}
TreeKind::WhileStatement => check_while_statement(s, tree),
} }
} }
} }
@ -2252,7 +2240,6 @@ fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {
let _ = s.type_of(t); // Checks arm count and compatibility. let _ = s.type_of(t); // Checks arm count and compatibility.
// TODO: completeness checks // TODO: completeness checks
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_pattern_analysis/usefulness/index.html
// let arms: Vec<_> = tree // let arms: Vec<_> = tree
// .children_of_kind(s.syntax_tree, TreeKind::MatchArm) // .children_of_kind(s.syntax_tree, TreeKind::MatchArm)
@ -2266,18 +2253,6 @@ fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) {
// } // }
} }
fn check_while_statement(s: &Semantics, tree: &Tree) {
if let Some(expr) = tree.nth_tree(1) {
let expr_type = s.type_of(expr);
if !s.can_convert(&expr_type, &Type::Bool) {
s.report_error_tree_ref(
expr,
"the condition of the while loop must produce a boolean",
);
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -430,41 +430,21 @@ fn eval_one(
f.push_string(new_string.into()); f.push_string(new_string.into());
} }
Instruction::EqBool => { Instruction::CompareBool => {
let x = f.pop_bool()?; let x = f.pop_bool()?;
let y = f.pop_bool()?; let y = f.pop_bool()?;
f.push_bool(x == y); f.push_bool(x == y);
} }
Instruction::EqFloat => { Instruction::CompareFloat => {
let x = f.pop_float()?; let x = f.pop_float()?;
let y = f.pop_float()?; let y = f.pop_float()?;
f.push_bool(x == y); f.push_bool(x == y);
} }
Instruction::EqString => { Instruction::CompareString => {
let x = f.pop_string()?; let x = f.pop_string()?;
let y = f.pop_string()?; let y = f.pop_string()?;
f.push_bool(x == y); f.push_bool(x == y);
} }
Instruction::GreaterFloat => {
let x = f.pop_float()?;
let y = f.pop_float()?;
f.push_bool(y > x);
}
Instruction::GreaterString => {
let x = f.pop_string()?;
let y = f.pop_string()?;
f.push_bool(y > x);
}
Instruction::LessFloat => {
let x = f.pop_float()?;
let y = f.pop_float()?;
f.push_bool(y < x);
}
Instruction::LessString => {
let x = f.pop_string()?;
let y = f.pop_string()?;
f.push_bool(y < x);
}
Instruction::NewObject(slots) => { Instruction::NewObject(slots) => {
let class_id = f.pop_int()?; let class_id = f.pop_int()?;
@ -516,16 +496,16 @@ pub fn eval(
let instructions = f.func.instructions(); let instructions = f.func.instructions();
let instruction = instructions[index]; let instruction = instructions[index];
// { {
// eprint!("{index}: {instruction:?} ["); eprint!("{index}: {instruction:?} [");
// for val in f.stack.iter().rev().take(3) { for val in f.stack.iter().rev().take(3) {
// eprint!("{val:?} "); eprint!("{val:?} ");
// } }
// if f.stack.len() > 3 { if f.stack.len() > 3 {
// eprint!("..."); eprint!("...");
// } }
// eprintln!("]"); eprintln!("]");
// } }
index += 1; index += 1;

View file

@ -46,7 +46,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
// from the `is` binding in scope. // from the `is` binding in scope.
if weapon is MeleeWeapon and distance > 1 or if weapon is MeleeWeapon and distance > 1 or
weapon is w : RangedWeapon and (distance < w.minRange or distance > w.maxRange) { weapon is w : RangedWeapon and (distance < w.minRange or distance > w.maxRange) {
print("You are out of range"); print("You are out of range")
return return
} }
@ -72,7 +72,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
} }
} }
fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or nothing { fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () {
if weapon is w: RangedWeapon and w.maxRange > 10 { if weapon is w: RangedWeapon and w.maxRange > 10 {
// w is still in scope here; the `and` is bound into a predicate expression // w is still in scope here; the `and` is bound into a predicate expression
// and breaks exhaustivity // and breaks exhaustivity
@ -115,7 +115,7 @@ fun test() -> f64 {
// Unroll by hand... // Unroll by hand...
let it = new Iterator { current: 0 }; let it = new Iterator { current: 0 };
while true { loop {
if it.next() is v: f64 { if it.next() is v: f64 {
sum = sum + v; sum = sum + v;
} else { } else {

View file

@ -16,7 +16,7 @@ fun test() {
// | 1: Return // | 1: Return
// | // |
// @eval: Nothing // @eval: Nothing
// @type: 15 nothing // @type: 15 ()
// @concrete: // @concrete:
// | File // | File
// | FunctionDecl // | FunctionDecl

View file

@ -1,4 +1,4 @@
if (if false { true }) { 32 } else { 23 } if (if false { true }) { 32 } else { 23 }
// @expect-errors: // @expect-errors:
// | 1:4: the type of the 'then' branch ('bool') must match the type of the 'else' branch ('nothing') // | 1:4: the type of the 'then' branch ('bool') must match the type of the 'else' branch ('()')

View file

@ -1,6 +0,0 @@
fun test() {
while 12 {}
}
// @expect-errors:
// | 2:8: the condition of the while loop must produce a boolean

View file

@ -1,12 +0,0 @@
fun test() -> f64 {
let result = 1;
let i = 0;
while i < 10 {
result = result * 2;
i = i + 1;
}
result
}
// @no-errors
// @eval: Float(1024.0)