Skip to content

Commit

Permalink
feat: Add seq expressions
Browse files Browse the repository at this point in the history
`seq` is the same as `do` but are written without binding any variable.
`do` were also generalized to support patterns.
  • Loading branch information
Marwes committed Jan 3, 2019
1 parent 3b87892 commit f452c2e
Show file tree
Hide file tree
Showing 21 changed files with 175 additions and 97 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ do digits =
wrap [a, b, c, d]
let print_digits = for digits (\d ->
do _ = io.print " "
do io.print " "
io.print (show d))
do _ = io.print "Four digits:" *> print_digits *> io.println ""
do io.print "Four digits:" *> print_digits *> io.println ""
let guess_loop _ =
do line = io.read_line
Expand Down
6 changes: 4 additions & 2 deletions base/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ pub struct ExprField<Id, E> {

#[derive(Clone, PartialEq, Debug)]
pub struct Do<Id> {
pub id: SpannedIdent<Id>,
pub id: Option<SpannedPattern<Id>>,
pub bound: Box<SpannedExpr<Id>>,
pub body: Box<SpannedExpr<Id>>,
pub flat_map_id: Option<Box<SpannedExpr<Id>>>,
Expand Down Expand Up @@ -685,7 +685,9 @@ pub fn walk_expr<'a, V: ?Sized + $trait_name<'a>>(v: &mut V, e: &'a $($mut)* Spa
ref $($mut)* body,
ref $($mut)* flat_map_id,
}) => {
v.visit_spanned_typed_ident(id);
if let Some(id) = id {
v.visit_pattern(id);
}
v.visit_expr(bound);
v.visit_expr(body);
if let Some(ref $($mut)* flat_map_id) = *flat_map_id {
Expand Down
12 changes: 9 additions & 3 deletions check/src/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::base::{
self, DisplayEnv, Do, Expr, MutVisitor, Pattern, SpannedAlias, SpannedAstType, SpannedExpr,
TypedIdent,
},
pos::{self, BytePos, Span},
pos::{self, ByteOffset, BytePos, Span},
scoped_map::ScopedMap,
symbol::{Symbol, SymbolModule},
types::Type,
Expand Down Expand Up @@ -229,7 +229,7 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
}) => {
let flat_map = self.symbols.symbol("flat_map");
*flat_map_id = Some(Box::new(pos::spanned(
id.span,
Span::new(expr.span.end(), expr.span.start() + ByteOffset::from(2)),
Expr::Ident(TypedIdent {
name: flat_map,
typ: Type::hole(),
Expand All @@ -245,7 +245,9 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {

self.env.stack.enter_scope();

id.value.name = self.stack_var(id.value.name.clone(), id.span);
if let Some(ref mut id) = *id {
self.visit_pattern(id);
}

return TailCall::TailCall;
}
Expand All @@ -259,6 +261,10 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
impl<'a, 'b, 'c> MutVisitor<'c> for RenameVisitor<'a, 'b> {
type Ident = Symbol;

fn visit_pattern(&mut self, pattern: &mut ast::SpannedPattern<Symbol>) {
self.new_pattern(pattern);
}

fn visit_expr(&mut self, mut expr: &mut SpannedExpr<Self::Ident>) {
let mut i = 0;
loop {
Expand Down
40 changes: 19 additions & 21 deletions check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ use crate::base::{
},
};

use crate::implicits;
use crate::kindcheck::{self, Error as KindCheckError, KindCheck, KindError};
use crate::substitution::{self, Substitution};
use crate::unify::{self, Error as UnifyError};
use crate::unify_type::{self, new_skolem_scope, Error as UnifyTypeError};
use crate::{
implicits,
kindcheck::{self, Error as KindCheckError, KindCheck, KindError},
substitution::{self, Substitution},
unify::{self, Error as UnifyError},
unify_type::{self, new_skolem_scope, Error as UnifyTypeError},
};

/// Type representing a single error when checking a type
#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -1307,6 +1309,7 @@ impl<'a> Typecheck<'a> {
ref mut body,
ref mut flat_map_id,
}) => {
let do_span = expr.span.subspan(0.into(), 2.into());
let flat_map_type = match flat_map_id
.as_mut()
.expect("flat_map inserted during renaming")
Expand All @@ -1316,7 +1319,7 @@ impl<'a> Typecheck<'a> {
Ok(x) => x,
Err(error) => {
self.error(
id.span,
do_span,
crate::base::error::Help {
error,
help: Some(Help::UndefinedFlatMapInDo),
Expand All @@ -1334,11 +1337,11 @@ impl<'a> Typecheck<'a> {
Type::Function(ArgType::Implicit, ref arg_type, ref r) => {
let name = self.implicit_resolver.make_implicit_ident(arg_type);
*flat_map_id = Some(Box::new(pos::spanned(
id.span,
do_span,
Expr::App {
func: flat_map_id.take().unwrap(),
args: vec![pos::spanned(
id.span,
do_span,
Expr::Ident(TypedIdent {
name,
typ: arg_type.clone(),
Expand All @@ -1352,31 +1355,26 @@ impl<'a> Typecheck<'a> {
_ => flat_map_type.clone(),
};

id.value.typ = self.subs.new_var();
let id_var = self.subs.new_var();
let arg1 = self
.type_cache
.function(Some(id.value.typ.clone()), self.subs.new_var());

.function(Some(id_var.clone()), self.subs.new_var());
let arg2 = self.subs.new_var();

let ret = expected_type
.cloned()
.unwrap_or_else(|| self.subs.new_var());
let func_type = self
.type_cache
.function(vec![arg1.clone(), arg2.clone()], ret.clone());

self.unify_span(expr.span, &flat_map_type, func_type);
self.unify_span(do_span, &flat_map_type, func_type);

let bound_type = self.typecheck(bound, &arg2);
self.typecheck(bound, &arg2);

self.unify_span(
// Point to the `do` token
expr.span.subspan(0.into(), 2.into()),
&arg2,
bound_type,
);

self.stack_var(id.value.name.clone(), id.value.typ.clone());
if let Some(ref mut id) = *id {
self.typecheck_pattern(id, id_var);
}

let body_type = self.typecheck(body, &ret);

Expand Down
15 changes: 15 additions & 0 deletions check/tests/pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,21 @@ test (x #Int+ 2)
assert_req!(result.map(support::close_record), expected);
}

test_check! {
do_expression_bind_scope,
r#"
type Test a = { x : a }
let flat_map f x : (a -> Test b) -> Test a -> Test b = f x.x
let test x : a -> Test a = { x }
let f y : () -> Test Int =
do y = test 1
test (y #Int+ 2)
f
"#,
"() -> test.Test Int"
}

#[test]
fn eq_unresolved_constraint_bug() {
let _ = env_logger::try_init();
Expand Down
14 changes: 6 additions & 8 deletions completion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,18 +678,16 @@ where
}
}
Expr::Do(ref do_expr) => {
let iter = once(Either::Left(&do_expr.id))
let iter = do_expr
.id
.as_ref()
.map(Either::Left)
.into_iter()
.chain(once(Either::Right(&do_expr.bound)))
.chain(once(Either::Right(&do_expr.body)));
match self.select_spanned(iter, |x| x.either(|i| i.span, |e| e.span)) {
(_, Some(either)) => match either {
Either::Left(ident) => {
self.found = MatchState::Found(Match::Ident(
ident.span,
&ident.value.name,
ident.value.typ.clone(),
));
}
Either::Left(ident) => self.visit_pattern(ident),
Either::Right(expr) => self.visit_expr(expr),
},
_ => unreachable!(),
Expand Down
4 changes: 2 additions & 2 deletions examples/24.glu
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ do digits =
wrap [a, b, c, d]

let print_digits = for digits (\d ->
do _ = io.print " "
do io.print " "
io.print (show d))
do _ = io.print "Four digits:" *> print_digits *> io.println ""
do io.print "Four digits:" *> print_digits *> io.println ""

let guess_loop _ =
do line = io.read_line
Expand Down
6 changes: 3 additions & 3 deletions examples/http/server.glu
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ let sum : Eff (HttpEffect r) Response =
do body = get_request >>= array_body
match string.from_utf8 body with
| Err _ ->
do _ = write_response (string.as_bytes "Request contained invalid UTF-8")
seq write_response (string.as_bytes "Request contained invalid UTF-8")
wrap { status = status.bad_request, .. http.response }
| Ok string_body ->
match de.deserialize de.deserializer string_body with
| Ok int_array ->
let int_array : Array Int = int_array
let s = foldl (+) 0 int_array
do _ = write_response (string.as_bytes (show s))
do write_response (string.as_bytes (show s))
wrap { status = status.ok, .. http.response }
| Err err ->
do _ = write_response (string.as_bytes err)
seq write_response (string.as_bytes err)
wrap { status = status.bad_request, .. http.response }


Expand Down
10 changes: 5 additions & 5 deletions examples/lisp/lisp.glu
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ let { fold_m } = import! std.foldable
let scope_state run : Eff (LispEffect r) a -> Eff (LispEffect r) a =
do original = get
do x = run
do _ = put original
seq put original
wrap x

let primitive name f : String -> _ -> Map String Expr = std_map.singleton name (Primitive f)
Expand Down Expand Up @@ -112,7 +112,7 @@ let define xs =
| Cons (Atom name) (Cons value Nil) ->
do state = get
let new_state = std_map.insert name value state
do _ = put new_state
seq put new_state
wrap value
| Cons (List (Cons (Atom name) params)) body ->
do closure = get
Expand All @@ -125,7 +125,7 @@ let define xs =
}
let new_state = std_map.insert name function closure

do _ = put new_state
seq put new_state

wrap function
| _ -> throw "Unexpected parameters to define `define`"
Expand All @@ -141,7 +141,7 @@ let apply f xs : forall r . Expr -> List Expr -> Eff (LispEffect r) Expr =
let add_args names values : List String -> _ =
match (names, values) with
| (Cons name names, Cons value values) ->
do _ = modify (\state -> std_map.insert name value state)
seq modify (\state -> std_map.insert name value state)
add_args names values
| (Nil, _) -> wrap ()
| _ -> throw "Not enough arguments to function"
Expand All @@ -150,7 +150,7 @@ let apply f xs : forall r . Expr -> List Expr -> Eff (LispEffect r) Expr =
| Primitive primitive -> primitive xs
| Function function ->
scope_state (
do _ = add_args function.params xs
seq add_args function.params xs
eval_exprs function.body)
| _ -> throw ("Can\'t call value: " <> show f)

Expand Down
2 changes: 1 addition & 1 deletion examples/marshalling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ fn marshal_generic() -> Result<()> {
let either: forall r . Either String r = Left "hello rust!"
// we can pass the generic Either to the Rust function without an issue
do _ =
do
match flip either with
| Left _ -> error "unreachable!"
| Right val -> io.println ("Right is: " <> val)
Expand Down
15 changes: 10 additions & 5 deletions format/src/pretty_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,11 +473,16 @@ where
..
}) => chain![arena;
chain![arena;
"do",
self.space_before(id.span.start()),
id.value.name.as_ref(),
self.space_after(id.span.end()),
"=",
match id {
Some(pattern) => chain![arena;
"do",
self.space_before(pattern.span.start()),
self.pretty_pattern(pattern),
self.space_after(pattern.span.end()),
"="
],
None => arena.text("seq"),
},
self.hang(arena.nil(), bound).group()
].group(),
self.pretty_expr_(bound.span.end(), body)
Expand Down
15 changes: 12 additions & 3 deletions parser/src/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::itertools::{Either, Itertools};

use crate::base::{
ast::{Alternative, Argument, Array, AstType, Do, Expr, ExprField, Lambda, Literal, Pattern,
PatternField, SpannedExpr, SpannedIdent, TypeBinding, TypedIdent, ValueBinding, ValueBindings},
PatternField, SpannedExpr, SpannedIdent, SpannedPattern, TypeBinding, TypedIdent, ValueBinding, ValueBindings},
kind::{ArcKind, Kind},
pos::{self, BytePos, Spanned},
types::{Alias, AliasData, ArcType, ArgType, BuiltinType, Field, Generic, Type, TypeCache},
Expand Down Expand Up @@ -42,6 +42,7 @@ extern {
"in" => Token::In,
"let" => Token::Let,
"do" => Token::Do,
"seq" => Token::Seq,
"match" => Token::Match,
"then" => Token::Then,
"type" => Token::Type,
Expand Down Expand Up @@ -753,8 +754,12 @@ Expr: Expr<Id> = {
Expr::TypeBindings(bindings, Box::new(body))
},

"do" <id: SpannedIdent> "=" <bound: SpExpr> <body: InExpr> => {
Expr::Do(Do { id, bound: Box::new(bound), body: Box::new(body), flat_map_id: None })
"do" <bind: DoBinding> <body: InExpr> => {
Expr::Do(Do { id: Some(bind.0), bound: Box::new(bind.1), body: Box::new(body), flat_map_id: None })
},

"seq" <bound: SpExpr> <body: InExpr> => {
Expr::Do(Do { id: None, bound: Box::new(bound), body: Box::new(body), flat_map_id: None })
},

BlockExpr,
Expand All @@ -765,6 +770,10 @@ Expr: Expr<Id> = {
}
};

DoBinding: (SpannedPattern<Id>, SpannedExpr<Id>) = {
<id: Sp<Pattern>> "=" <bound: SpExpr> => (id, bound),
};

BlockExpr: Expr<Id> = {
"block open" <exprs: (<SpExpr> "block separator")*> <last: SpExpr> "block close" => {
let mut exprs = exprs;
Expand Down
2 changes: 1 addition & 1 deletion parser/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ where
Token::Rec => Some(Context::Rec),
Token::Type => Some(Context::Type),
Token::Let => Some(Context::Let),
Token::Do => Some(Context::Let),
Token::Do | Token::Seq => Some(Context::Let),
Token::If => Some(Context::If),
Token::Match => Some(Context::Expr),
Token::Lambda => Some(Context::Lambda),
Expand Down
Loading

0 comments on commit f452c2e

Please sign in to comment.