summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-04-29 20:49:55 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-04-29 21:15:42 +0200
commitbcec2e72909bc63bdc2cb88808789a04f3a13d72 (patch)
treedd6a0437ecc216398c324b3ce5ccea1559227831
parent62abc9e8059a70b2f8106238d724428af9bda76b (diff)
downloadrebel-bcec2e72909bc63bdc2cb88808789a04f3a13d72.tar
rebel-bcec2e72909bc63bdc2cb88808789a04f3a13d72.zip
rebel-parse, rebel-lang: distinguish let statements and simple assignments
-rw-r--r--crates/rebel-lang/src/scope.rs76
-rw-r--r--crates/rebel-lang/src/typing.rs22
-rw-r--r--crates/rebel-parse/src/ast/mod.rs24
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs10
4 files changed, 115 insertions, 17 deletions
diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs
index c57fd3c..0a906de 100644
--- a/crates/rebel-lang/src/scope.rs
+++ b/crates/rebel-lang/src/scope.rs
@@ -1,4 +1,4 @@
-use std::{collections::HashMap, ops::Deref};
+use std::collections::HashMap;
use rebel_parse::ast::{self, pat};
@@ -44,19 +44,48 @@ impl Default for Context {
impl Context {
pub fn record_type(&mut self, stmt: &ast::BlockStmt) -> typing::Result<Type> {
match stmt {
- ast::BlockStmt::Assign { dest, expr } => {
- let typ = Type::ast_expr_type(self, expr)?;
+ ast::BlockStmt::Let { dest, expr } => {
+ let typ = if let Some(expr) = expr {
+ Type::ast_expr_type(self, expr)?
+ } else {
+ Type::Free
+ };
// TODO: Handle explicit type
- let ast::TypedPat { pat, typ: _ } = dest.deref();
+ let ast::TypedPat { pat, typ: _ } = dest.as_ref();
- // TODO: Handle other assignable expressions
let pat::Pat::Path(dest_path) = pat;
let [dest_ident] = dest_path.components[..] else {
return Err(TypeError);
};
// TODO: Lexical scoping
+ // TODO: Typechecking
+ self.vars.0.insert(
+ dest_ident.name.to_owned(),
+ ModuleEntry::Def(Var {
+ explicit_type: Type::Free,
+ inferred_type: typ.clone(),
+ value: None,
+ }),
+ );
+
+ Ok(typ)
+ }
+ ast::BlockStmt::Assign { dest, expr } => {
+ let typ = Type::ast_expr_type(self, expr)?;
+
+ let pat::Pat::Path(dest_path) = dest.as_ref();
+ let [dest_ident] = dest_path.components[..] else {
+ return Err(TypeError);
+ };
+
+ if !self.vars.0.contains_key(dest_ident.name) {
+ return Err(TypeError);
+ }
+
+ // TODO: Lexical scoping
+ // TODO: Typechecking
self.vars.0.insert(
dest_ident.name.to_owned(),
ModuleEntry::Def(Var {
@@ -75,15 +104,44 @@ impl Context {
pub fn execute(&mut self, stmt: &ast::BlockStmt) -> value::Result<Value> {
match stmt {
+ ast::BlockStmt::Let { dest, expr } => {
+ let (typ, value) = if let Some(expr) = expr {
+ let value = Value::eval(self, expr)?;
+ (value.typ().or(Err(EvalError))?, Some(value))
+ } else {
+ (Type::Free, None)
+ };
+
+ // TODO: Handle explicit type
+ let ast::TypedPat { pat, typ: _ } = dest.as_ref();
+
+ let pat::Pat::Path(dest_path) = pat;
+ let [dest_ident] = dest_path.components[..] else {
+ return Err(EvalError);
+ };
+
+ if dest_ident.name == "_" {
+ return Ok(Value::Unit);
+ }
+
+ // TODO: Lexical scoping
+ self.vars.0.insert(
+ dest_ident.name.to_owned(),
+ ModuleEntry::Def(Var {
+ explicit_type: Type::Free,
+ inferred_type: typ,
+ value,
+ }),
+ );
+
+ Ok(Value::Unit)
+ }
ast::BlockStmt::Assign { dest, expr } => {
let value = Value::eval(self, expr)?;
let typ = value.typ().or(Err(EvalError))?;
// TODO: Handle explicit type
- let ast::TypedPat { pat, typ: _ } = dest.deref();
-
- // TODO: Handle other assignable expressions
- let pat::Pat::Path(dest_path) = pat;
+ let pat::Pat::Path(dest_path) = dest.as_ref();
let [dest_ident] = dest_path.components[..] else {
return Err(EvalError);
};
diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs
index 6bb0fb5..70fc759 100644
--- a/crates/rebel-lang/src/typing.rs
+++ b/crates/rebel-lang/src/typing.rs
@@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Display};
use enum_kinds::EnumKind;
-use rebel_parse::ast::{self, expr, typ};
+use rebel_parse::ast::{self, expr, pat, typ};
use crate::{func::FuncType, scope::Context};
@@ -125,9 +125,25 @@ impl Type {
pub fn ast_stmt_type(ctx: &Context, stmt: &ast::BlockStmt<'_>) -> Result<Type> {
match stmt {
- ast::BlockStmt::Assign { dest: _, expr } => {
+ ast::BlockStmt::Let { dest: _, expr } => {
// TODO: Assignability, dest type
- let dest_type = Type::Free;
+ let mut dest_type = Type::Free;
+ if let Some(expr) = expr {
+ let expr_type = Self::ast_expr_type(ctx, expr)?;
+ dest_type = dest_type.unify(expr_type, Coerce::Assign)?;
+ }
+ Ok(dest_type)
+ }
+ ast::BlockStmt::Assign { dest, expr } => {
+ // TODO: Assignability
+ let pat::Pat::Path(dest_path) = dest.as_ref();
+
+ let dest_type = ctx
+ .vars
+ .lookup(&dest_path.components)
+ .map(|var| var.inferred_type.clone())
+ .ok_or(TypeError)?;
+
let expr_type = Self::ast_expr_type(ctx, expr)?;
dest_type.unify(expr_type, Coerce::Assign)
}
diff --git a/crates/rebel-parse/src/ast/mod.rs b/crates/rebel-parse/src/ast/mod.rs
index ebf7241..4fa8e63 100644
--- a/crates/rebel-parse/src/ast/mod.rs
+++ b/crates/rebel-parse/src/ast/mod.rs
@@ -53,8 +53,12 @@ impl<'a> Block<'a> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlockStmt<'a> {
- Assign {
+ Let {
dest: Box<TypedPat<'a>>,
+ expr: Option<Box<Expr<'a>>>,
+ },
+ Assign {
+ dest: Box<Pat<'a>>,
expr: Box<Expr<'a>>,
},
Expr {
@@ -64,15 +68,22 @@ pub enum BlockStmt<'a> {
}
impl<'a> BlockStmt<'a> {
+ pub(crate) fn let_assign(dest: TypedPat<'a>, expr: Option<Expr<'a>>) -> Self {
+ BlockStmt::Let {
+ dest: Box::new(dest),
+ expr: expr.map(Box::new),
+ }
+ }
+
pub(crate) fn assign(
- dest: TypedPat<'a>,
+ dest: Pat<'a>,
op: Option<OpBinary>,
swapped: bool,
expr: Expr<'a>,
) -> Self {
let expr = match op {
Some(op) => {
- let dest_expr = match &dest.pat {
+ let dest_expr = match &dest {
Pat::Path(path) => Expr::Path(path.clone()),
};
if swapped {
@@ -91,6 +102,13 @@ impl<'a> BlockStmt<'a> {
pub fn validate(&self) -> Result<(), ValidationError> {
match self {
+ BlockStmt::Let { dest: _, expr } => {
+ // TODO: Destination validation
+ if let Some(expr) = expr {
+ expr.validate()?;
+ }
+ Ok(())
+ }
BlockStmt::Assign { dest: _, expr } => {
// TODO: Destination validation
expr.validate()
diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs
index 6b96c85..c9fe1e2 100644
--- a/crates/rebel-parse/src/grammar/recipe.rs
+++ b/crates/rebel-parse/src/grammar/recipe.rs
@@ -34,10 +34,16 @@ peg::parser! {
= block:block_stmt() ++ p(';') { ast::Block(block) }
pub rule block_stmt() -> ast::BlockStmt<'a>
- = dest:typed_pat() op:assign_op() expr:expr() {
+ = [Token::Keyword(Keyword::Let)] dest:typed_pat() p('=') expr:expr() {
+ ast::BlockStmt::let_assign(dest, Some(expr))
+ }
+ / [Token::Keyword(Keyword::Let)] dest:typed_pat() {
+ ast::BlockStmt::let_assign(dest, None)
+ }
+ / dest:pat() op:assign_op() expr:expr() {
ast::BlockStmt::assign(dest, op, false, expr)
}
- / dest:typed_pat() p2('=', '+') expr:expr() {
+ / dest:pat() p2('=', '+') expr:expr() {
ast::BlockStmt::assign(dest, Some(Add), true, expr)
}
/ expr:expr() {