added if expression
This commit is contained in:
@@ -38,7 +38,7 @@ This language is under active development, progress will be marked here as the l
|
||||
- [ ] Higher kinded types
|
||||
- [ ] Variadic generic types
|
||||
- [ ] Control Flow
|
||||
- [ ] If
|
||||
- [x] If
|
||||
- [ ] While
|
||||
- [ ] For
|
||||
- [ ] Enums
|
||||
|
||||
@@ -65,8 +65,20 @@ impl User {
|
||||
}
|
||||
}
|
||||
|
||||
fn if_expression(): i64 {
|
||||
if (true) {
|
||||
return 6;
|
||||
} else {
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
|
||||
fn main(): i64 {
|
||||
add(4, 4)
|
||||
if (false) {
|
||||
add(4, 4)
|
||||
} else {
|
||||
61
|
||||
}
|
||||
}
|
||||
|
||||
// type TestTrait trait {
|
||||
|
||||
16
src/ast.rs
16
src/ast.rs
@@ -128,6 +128,12 @@ pub struct LiteralFloat {
|
||||
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)]
|
||||
pub struct LiteralStruct {
|
||||
pub name: Identifier,
|
||||
@@ -167,13 +173,23 @@ pub struct VariableUsage {
|
||||
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)]
|
||||
pub enum Subexpression {
|
||||
LiteralInt(LiteralInt),
|
||||
LiteralFloat(LiteralFloat),
|
||||
LiteralBool(LiteralBool),
|
||||
LiteralStruct(LiteralStruct),
|
||||
FunctionCall(FunctionCall),
|
||||
VariableUsage(VariableUsage),
|
||||
If(IfExpression),
|
||||
StructGetter(StructGetter),
|
||||
Block(Block),
|
||||
Op(Operation),
|
||||
|
||||
@@ -31,6 +31,10 @@ pub enum TypingError {
|
||||
FunctionCallNotAFunction {
|
||||
// TODO: add position
|
||||
},
|
||||
#[error("`if` condition must be bool")]
|
||||
IfConditionMustBeBool {
|
||||
// TODO: add position
|
||||
},
|
||||
#[error("multiple errors")]
|
||||
MultipleErrors { errors: Vec<TypingError> },
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ match {
|
||||
"fn",
|
||||
"return",
|
||||
"let",
|
||||
"true",
|
||||
"false",
|
||||
"if",
|
||||
"else",
|
||||
"=",
|
||||
"type",
|
||||
"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())}
|
||||
};
|
||||
|
||||
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 = {
|
||||
<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)}
|
||||
};
|
||||
|
||||
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 = {
|
||||
<l:Expression> "+" <r:Factor> => {
|
||||
ast::Expression{
|
||||
@@ -114,10 +132,12 @@ pub Factor: ast::Expression = {
|
||||
pub Term: ast::Expression = {
|
||||
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)},
|
||||
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)},
|
||||
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)},
|
||||
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)},
|
||||
"(" <e:Expression> ")" => e,
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@ pub enum Function {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Numeric(NumericValue),
|
||||
Bool(bool),
|
||||
Function(Function),
|
||||
Struct(Arc<Mutex<StructValue>>),
|
||||
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(
|
||||
"!".to_string(),
|
||||
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
|
||||
@@ -351,6 +361,14 @@ impl TreeWalkInterpreter {
|
||||
let value: f64 = literal_float.value.value.parse().unwrap();
|
||||
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) => {
|
||||
let declaration = match &ctx.environment[&literal_struct.name.name.value] {
|
||||
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(declaration)) => {
|
||||
@@ -423,6 +441,28 @@ impl TreeWalkInterpreter {
|
||||
};
|
||||
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) => {
|
||||
let source = match self.with_expression(ctx, &struct_getter.source) {
|
||||
ExpressionResult::Value(r) => r,
|
||||
|
||||
@@ -55,10 +55,10 @@ fn main() {
|
||||
let module_ast = grammar::ModuleParser::new()
|
||||
.parse(&unknown_id_gen, &contents)
|
||||
.unwrap(); //TODO: convert to error
|
||||
println!("ast: {:#?}", &module_ast);
|
||||
// println!("ast: {:#?}", &module_ast);
|
||||
let alias_resolver = type_alias_resolution::TypeAliasResolver {};
|
||||
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_checking_result = type_checker.with_module(&resolved_ast);
|
||||
match &type_checking_result {
|
||||
|
||||
@@ -258,6 +258,12 @@ impl TypeAliasResolver {
|
||||
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) => {
|
||||
let result = resolve_type(
|
||||
ctx,
|
||||
@@ -296,6 +302,17 @@ impl TypeAliasResolver {
|
||||
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(ast::StructGetter {
|
||||
source: self.with_expression(ctx, &struct_getter.source),
|
||||
|
||||
@@ -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(
|
||||
"!".to_string(),
|
||||
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
|
||||
@@ -132,7 +141,7 @@ fn create_builtins() -> HashMap<String, NamedEntity> {
|
||||
"unit".to_string(),
|
||||
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(
|
||||
ast::PrimitiveTypeDeclaration {
|
||||
name: "!".to_string(),
|
||||
name: "unit".to_string(),
|
||||
},
|
||||
)),
|
||||
);
|
||||
@@ -798,6 +807,17 @@ impl TypeChecker {
|
||||
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) => {
|
||||
substitution = compose_substitutions(
|
||||
ctx,
|
||||
@@ -926,6 +946,109 @@ impl TypeChecker {
|
||||
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) => {
|
||||
let (source, subst) =
|
||||
self.with_expression(ctx, &substitution, &struct_getter.source)?;
|
||||
|
||||
Reference in New Issue
Block a user