added if expression

This commit is contained in:
Andrew Segavac
2021-09-14 21:15:39 -06:00
parent e00aef5ef3
commit 26e477204a
9 changed files with 237 additions and 5 deletions

View File

@@ -38,7 +38,7 @@ This language is under active development, progress will be marked here as the l
- [ ] Higher kinded types - [ ] Higher kinded types
- [ ] Variadic generic types - [ ] Variadic generic types
- [ ] Control Flow - [ ] Control Flow
- [ ] If - [x] If
- [ ] While - [ ] While
- [ ] For - [ ] For
- [ ] Enums - [ ] Enums

View File

@@ -65,8 +65,20 @@ impl User {
} }
} }
fn if_expression(): i64 {
if (true) {
return 6;
} else {
return 9;
}
}
fn main(): i64 { fn main(): i64 {
if (false) {
add(4, 4) add(4, 4)
} else {
61
}
} }
// type TestTrait trait { // type TestTrait trait {

View File

@@ -128,6 +128,12 @@ pub struct LiteralFloat {
pub type_: TypeUsage, pub type_: TypeUsage,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralBool {
pub value: Spanned<String>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralStruct { pub struct LiteralStruct {
pub name: Identifier, pub name: Identifier,
@@ -167,13 +173,23 @@ pub struct VariableUsage {
pub type_: TypeUsage, pub type_: TypeUsage,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IfExpression {
pub condition: Expression,
pub block: Block,
pub else_: Option<Block>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Subexpression { pub enum Subexpression {
LiteralInt(LiteralInt), LiteralInt(LiteralInt),
LiteralFloat(LiteralFloat), LiteralFloat(LiteralFloat),
LiteralBool(LiteralBool),
LiteralStruct(LiteralStruct), LiteralStruct(LiteralStruct),
FunctionCall(FunctionCall), FunctionCall(FunctionCall),
VariableUsage(VariableUsage), VariableUsage(VariableUsage),
If(IfExpression),
StructGetter(StructGetter), StructGetter(StructGetter),
Block(Block), Block(Block),
Op(Operation), Op(Operation),

View File

@@ -31,6 +31,10 @@ pub enum TypingError {
FunctionCallNotAFunction { FunctionCallNotAFunction {
// TODO: add position // TODO: add position
}, },
#[error("`if` condition must be bool")]
IfConditionMustBeBool {
// TODO: add position
},
#[error("multiple errors")] #[error("multiple errors")]
MultipleErrors { errors: Vec<TypingError> }, MultipleErrors { errors: Vec<TypingError> },
} }

View File

@@ -20,6 +20,10 @@ match {
"fn", "fn",
"return", "return",
"let", "let",
"true",
"false",
"if",
"else",
"=", "=",
"type", "type",
"struct", "struct",
@@ -47,6 +51,15 @@ pub SpannedLiteralFloat: ast::LiteralFloat = {
<literal_float:Spanned<LiteralFloat>> => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())} <literal_float:Spanned<LiteralFloat>> => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())}
}; };
pub LiteralBool: String = {
"true" => "true".to_string(),
"false" => "false".to_string(),
};
pub SpannedLiteralBool: ast::LiteralBool = {
<literal_bool:Spanned<LiteralBool>> => ast::LiteralBool{value: literal_bool, type_: ast::TypeUsage::new_builtin("bool".to_string())}
};
pub Identifier: String = { pub Identifier: String = {
<i:r"[A-Za-z_][A-Za-z0-9_]*"> => i.to_string() <i:r"[A-Za-z_][A-Za-z0-9_]*"> => i.to_string()
}; };
@@ -79,6 +92,11 @@ pub VariableUsage: ast::VariableUsage = {
<identifier:SpannedIdentifier> => ast::VariableUsage{name: identifier, type_: ast::TypeUsage::new_unknown(&id_generator)} <identifier:SpannedIdentifier> => ast::VariableUsage{name: identifier, type_: ast::TypeUsage::new_unknown(&id_generator)}
}; };
pub IfExpression: ast::IfExpression = {
"if" "("<c:Expression>")" <b:Block> => ast::IfExpression{condition: c, block: b, else_: None, type_: ast::TypeUsage::new_unknown(&id_generator)},
"if" "("<c:Expression>")" <b:Block> "else" <e:Block> => ast::IfExpression{condition: c, block: b, else_: Some(e), type_: ast::TypeUsage::new_unknown(&id_generator)},
};
pub Expression: ast::Expression = { pub Expression: ast::Expression = {
<l:Expression> "+" <r:Factor> => { <l:Expression> "+" <r:Factor> => {
ast::Expression{ ast::Expression{
@@ -114,10 +132,12 @@ pub Factor: ast::Expression = {
pub Term: ast::Expression = { pub Term: ast::Expression = {
SpannedLiteralInt => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralInt(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, SpannedLiteralInt => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralInt(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
SpannedLiteralFloat => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralFloat(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, SpannedLiteralFloat => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralFloat(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
SpannedLiteralBool => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralBool(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
LiteralStruct => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralStruct(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, LiteralStruct => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralStruct(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
FunctionCall => ast::Expression{subexpression: Box::new(ast::Subexpression::FunctionCall(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, FunctionCall => ast::Expression{subexpression: Box::new(ast::Subexpression::FunctionCall(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
StructGetter => ast::Expression{subexpression: Box::new(ast::Subexpression::StructGetter(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, StructGetter => ast::Expression{subexpression: Box::new(ast::Subexpression::StructGetter(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
VariableUsage => ast::Expression{subexpression: Box::new(ast::Subexpression::VariableUsage(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, VariableUsage => ast::Expression{subexpression: Box::new(ast::Subexpression::VariableUsage(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
IfExpression => ast::Expression{subexpression: Box::new(ast::Subexpression::If(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
Block => ast::Expression{subexpression: Box::new(ast::Subexpression::Block(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, Block => ast::Expression{subexpression: Box::new(ast::Subexpression::Block(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
"(" <e:Expression> ")" => e, "(" <e:Expression> ")" => e,
}; };

View File

@@ -37,6 +37,7 @@ pub enum Function {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
Numeric(NumericValue), Numeric(NumericValue),
Bool(bool),
Function(Function), Function(Function),
Struct(Arc<Mutex<StructValue>>), Struct(Arc<Mutex<StructValue>>),
Unit, Unit,
@@ -204,6 +205,15 @@ fn create_builtins() -> HashMap<String, NamedEntity> {
)), )),
); );
result.insert(
"bool".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
ast::PrimitiveTypeDeclaration {
name: "bool".to_string(),
},
)),
);
result.insert( result.insert(
"!".to_string(), "!".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive( NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
@@ -351,6 +361,14 @@ impl TreeWalkInterpreter {
let value: f64 = literal_float.value.value.parse().unwrap(); let value: f64 = literal_float.value.value.parse().unwrap();
return ExpressionResult::Value(Value::Numeric(NumericValue::F64(value))); return ExpressionResult::Value(Value::Numeric(NumericValue::F64(value)));
} }
ast::Subexpression::LiteralBool(literal_bool) => {
let value: bool = if &literal_bool.value.value == "true" {
true
} else {
false
};
return ExpressionResult::Value(Value::Bool(value));
}
ast::Subexpression::LiteralStruct(literal_struct) => { ast::Subexpression::LiteralStruct(literal_struct) => {
let declaration = match &ctx.environment[&literal_struct.name.name.value] { let declaration = match &ctx.environment[&literal_struct.name.name.value] {
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(declaration)) => { NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(declaration)) => {
@@ -423,6 +441,28 @@ impl TreeWalkInterpreter {
}; };
return ExpressionResult::Value(variable_value); return ExpressionResult::Value(variable_value);
} }
ast::Subexpression::If(if_expression) => {
let condition = match self.with_expression(ctx, &if_expression.condition) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
match &condition {
Value::Bool(cond) => {
if cond.clone() {
return self.with_block(ctx, &if_expression.block);
} else {
return match &if_expression.else_ {
Some(else_) => self.with_block(ctx, else_),
None => ExpressionResult::Value(Value::Unit),
};
}
}
_ => panic!("TypeError: condition must be bool"),
}
}
ast::Subexpression::StructGetter(struct_getter) => { ast::Subexpression::StructGetter(struct_getter) => {
let source = match self.with_expression(ctx, &struct_getter.source) { let source = match self.with_expression(ctx, &struct_getter.source) {
ExpressionResult::Value(r) => r, ExpressionResult::Value(r) => r,

View File

@@ -55,10 +55,10 @@ fn main() {
let module_ast = grammar::ModuleParser::new() let module_ast = grammar::ModuleParser::new()
.parse(&unknown_id_gen, &contents) .parse(&unknown_id_gen, &contents)
.unwrap(); //TODO: convert to error .unwrap(); //TODO: convert to error
println!("ast: {:#?}", &module_ast); // println!("ast: {:#?}", &module_ast);
let alias_resolver = type_alias_resolution::TypeAliasResolver {}; let alias_resolver = type_alias_resolution::TypeAliasResolver {};
let resolved_ast = alias_resolver.with_module(&module_ast); let resolved_ast = alias_resolver.with_module(&module_ast);
println!("resolved ast: {:#?}", &resolved_ast); // println!("resolved ast: {:#?}", &resolved_ast);
let type_checker = type_checking::TypeChecker {}; let type_checker = type_checking::TypeChecker {};
let type_checking_result = type_checker.with_module(&resolved_ast); let type_checking_result = type_checker.with_module(&resolved_ast);
match &type_checking_result { match &type_checking_result {

View File

@@ -258,6 +258,12 @@ impl TypeAliasResolver {
type_: process_type(ctx, &literal_float.type_), type_: process_type(ctx, &literal_float.type_),
}) })
} }
ast::Subexpression::LiteralBool(literal_bool) => {
ast::Subexpression::LiteralBool(ast::LiteralBool {
value: literal_bool.value.clone(),
type_: process_type(ctx, &literal_bool.type_),
})
}
ast::Subexpression::LiteralStruct(literal_struct) => { ast::Subexpression::LiteralStruct(literal_struct) => {
let result = resolve_type( let result = resolve_type(
ctx, ctx,
@@ -296,6 +302,17 @@ impl TypeAliasResolver {
type_: process_type(ctx, &variable_usage.type_), type_: process_type(ctx, &variable_usage.type_),
}) })
} }
ast::Subexpression::If(if_expression) => {
ast::Subexpression::If(ast::IfExpression {
condition: self.with_expression(ctx, &if_expression.condition),
block: self.with_block(ctx, &if_expression.block),
else_: match &if_expression.else_ {
Some(else_) => Some(self.with_block(ctx, else_)),
None => None,
},
type_: process_type(ctx, &if_expression.type_),
})
}
ast::Subexpression::StructGetter(struct_getter) => { ast::Subexpression::StructGetter(struct_getter) => {
ast::Subexpression::StructGetter(ast::StructGetter { ast::Subexpression::StructGetter(ast::StructGetter {
source: self.with_expression(ctx, &struct_getter.source), source: self.with_expression(ctx, &struct_getter.source),

View File

@@ -120,6 +120,15 @@ fn create_builtins() -> HashMap<String, NamedEntity> {
)), )),
); );
result.insert(
"bool".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
ast::PrimitiveTypeDeclaration {
name: "bool".to_string(),
},
)),
);
result.insert( result.insert(
"!".to_string(), "!".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive( NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
@@ -132,7 +141,7 @@ fn create_builtins() -> HashMap<String, NamedEntity> {
"unit".to_string(), "unit".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive( NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
ast::PrimitiveTypeDeclaration { ast::PrimitiveTypeDeclaration {
name: "!".to_string(), name: "unit".to_string(),
}, },
)), )),
); );
@@ -798,6 +807,17 @@ impl TypeChecker {
type_: apply_substitution(ctx, &substitution, &literal_float.type_)?, type_: apply_substitution(ctx, &substitution, &literal_float.type_)?,
}) })
} }
ast::Subexpression::LiteralBool(literal_bool) => {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &expression.type_, &literal_bool.type_)?,
)?;
ast::Subexpression::LiteralBool(ast::LiteralBool {
value: literal_bool.value.clone(),
type_: apply_substitution(ctx, &substitution, &literal_bool.type_)?,
})
}
ast::Subexpression::LiteralStruct(literal_struct) => { ast::Subexpression::LiteralStruct(literal_struct) => {
substitution = compose_substitutions( substitution = compose_substitutions(
ctx, ctx,
@@ -926,6 +946,109 @@ impl TypeChecker {
type_: apply_substitution(ctx, &substitution, &variable_usage.type_)?, type_: apply_substitution(ctx, &substitution, &variable_usage.type_)?,
}) })
} }
ast::Subexpression::If(if_expression) => {
let (condition, subst) =
self.with_expression(ctx, &substitution, &if_expression.condition)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
let (block_result, subst) =
self.with_block(ctx, &substitution, &if_expression.block)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
let else_ = match &if_expression.else_ {
Some(else_) => {
let (result, subst) = self.with_block(ctx, &substitution, else_)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
Some(result)
}
None => None,
};
match &condition.type_ {
ast::TypeUsage::Named(named) => {
if named.name.name.value != "bool" {
return Err(errors::TypingError::IfConditionMustBeBool {});
}
}
ast::TypeUsage::Function(_) => {
return Err(errors::TypingError::IfConditionMustBeBool {});
}
_ => {}
};
let mut never_count = 0;
match &block_result.type_ {
ast::TypeUsage::Named(named) => {
if named.name.name.value != "!" {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &if_expression.type_, &block_result.type_)?,
)?;
} else {
never_count += 1;
}
}
_ => {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &if_expression.type_, &block_result.type_)?,
)?;
}
};
match &else_ {
Some(else_block) => {
match &else_block.type_ {
ast::TypeUsage::Named(named) => {
if named.name.name.value != "!" {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &if_expression.type_, &else_block.type_)?,
)?;
} else {
never_count += 1;
}
}
_ => {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &if_expression.type_, &else_block.type_)?,
)?;
}
};
}
None => {
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &if_expression.type_, &ast::new_unit())?,
)?;
}
}
let result_type = if never_count == 2 {
ast::new_never()
} else {
apply_substitution(ctx, &substitution, &if_expression.type_)?
};
substitution = compose_substitutions(
ctx,
&substitution,
&unify(ctx, &expression.type_, &result_type)?,
)?;
ast::Subexpression::If(ast::IfExpression {
condition: condition,
block: block_result,
else_: else_,
type_: result_type,
})
}
ast::Subexpression::StructGetter(struct_getter) => { ast::Subexpression::StructGetter(struct_getter) => {
let (source, subst) = let (source, subst) =
self.with_expression(ctx, &substitution, &struct_getter.source)?; self.with_expression(ctx, &substitution, &struct_getter.source)?;