Skip to content

Commit 466fca0

Browse files
committed
feat: add expression pipe syntax (|>)
1 parent 1abea96 commit 466fca0

File tree

13 files changed

+169
-45
lines changed

13 files changed

+169
-45
lines changed

examples/lib/std.lo

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,9 @@ fn str::skip_chars(self, char_count: u32): str {
655655

656656
fn str::slice(self, start: u32, end: u32): str {
657657
if start >= self.size || end > self.size || start > end {
658-
return self
658+
return ""
659659
}
660+
660661
return str {
661662
data: (self.data as u32 + start) as *&u8,
662663
size: end - start,

examples/self-hosted/_bin.lo

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,9 @@ export fn _start() {
6262
" ", \\ token.type_.to_str(),
6363

6464
" ", \\ ">> ",
65-
String::from_str_in(a, token.loc.read_span(lexer.source)) \
66-
.replace("\n", "\\n").as_str(),
67-
// TODO: I'd like to see it like this:
68-
// token.loc.read_span(lexer.source) \
69-
// |> String::from_str_in(a, it).replace("\n", "\\n")
70-
// |> it.as_str(),
65+
token.loc.read_span(lexer.source) \
66+
|> String::from_str_in(a, it).replace("\n", "\\n") \
67+
|> it.as_str(),
7168
" <<",
7269

7370
"\n",

examples/test/expr-pipe.lo

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn main(): u32 {
2+
return 2 \
3+
|> it + 2 \
4+
|> mul(it, it) \
5+
|> it.sub(2)
6+
}
7+
8+
fn mul(a: u32, b: u32): u32 {
9+
return a * b
10+
}
11+
12+
fn u32::sub(self, x: u32): u32 {
13+
return self - x
14+
}

lo.wasm

3.48 KB
Binary file not shown.

src/ast.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,13 @@ pub enum CodeExpr {
254254
Break(BreakExpr),
255255
ForLoop(ForLoopExpr),
256256
Continue(ContinueExpr),
257-
DoWith(DoWithExpr),
258257
Defer(DeferExpr),
259258
Catch(CatchExpr),
260259
Match(MatchExpr),
261260

261+
DoWith(DoWithExpr),
262+
ExprPipe(ExprPipeExpr),
263+
262264
// TODO?: should these use intrinsic syntax?
263265
Dbg(DbgExpr),
264266
Sizeof(SizeofExpr),
@@ -378,6 +380,13 @@ pub struct DoWithExpr {
378380
pub loc: LoLocation,
379381
}
380382

383+
pub struct ExprPipeExpr {
384+
pub lhs: Box<CodeExpr>,
385+
pub rhs: Box<CodeExpr>,
386+
pub op_loc: LoLocation,
387+
pub loc: LoLocation,
388+
}
389+
381390
pub struct DbgExpr {
382391
pub message: EscapedString,
383392
pub loc: LoLocation,
@@ -532,6 +541,7 @@ impl CodeExpr {
532541
CodeExpr::PropagateError(e) => &e.loc,
533542
CodeExpr::PrefixOp(e) => &e.loc,
534543
CodeExpr::DoWith(e) => &e.loc,
544+
CodeExpr::ExprPipe(e) => &e.loc,
535545
}
536546
}
537547
}

src/compiler.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,7 @@ impl Compiler {
16821682
}
16831683
}
16841684

1685+
// TODO: make this report errors instead of returning first error
16851686
fn codegen(
16861687
&self,
16871688
ctx: &mut LoExprContext,
@@ -2772,10 +2773,7 @@ impl Compiler {
27722773
return Ok(());
27732774
};
27742775

2775-
ctx.enter_scope(LoScopeType::Block);
27762776
let arg_type = self.get_expr_type(ctx, first_arg)?;
2777-
let arg_local_index =
2778-
self.define_local(ctx, with_loc.clone(), String::from("it"), &arg_type, false)?;
27792777

27802778
for arg in &args.items {
27812779
let current_arg_type = self.get_expr_type(ctx, arg)?;
@@ -2791,11 +2789,46 @@ impl Compiler {
27912789
continue;
27922790
}
27932791

2792+
ctx.enter_scope(LoScopeType::Block);
2793+
27942794
self.codegen(ctx, instrs, arg)?;
2795+
2796+
let arg_local_index = self.define_local(
2797+
ctx,
2798+
with_loc.clone(),
2799+
String::from("it"),
2800+
&arg_type,
2801+
false,
2802+
)?;
2803+
27952804
self.codegen_local_set(instrs, &arg_type, arg_local_index);
27962805
self.codegen(ctx, instrs, body)?;
2806+
2807+
ctx.exit_scope();
27972808
}
2809+
}
2810+
CodeExpr::ExprPipe(ExprPipeExpr {
2811+
lhs,
2812+
rhs,
2813+
op_loc,
2814+
loc: _,
2815+
}) => {
2816+
let lhs_type = self.get_expr_type(ctx, lhs)?;
2817+
catch!(self.codegen(ctx, instrs, lhs), err, {
2818+
self.report_error(&err);
2819+
return Ok(());
2820+
});
2821+
2822+
ctx.enter_scope(LoScopeType::Block);
2823+
2824+
let lhs_local_index =
2825+
self.define_local(ctx, op_loc.clone(), String::from("it"), &lhs_type, false)?;
27982826

2827+
self.codegen_local_set(instrs, &lhs_type, lhs_local_index);
2828+
catch!(self.codegen(ctx, instrs, rhs), err, {
2829+
self.report_error(&err);
2830+
return Ok(());
2831+
});
27992832
ctx.exit_scope();
28002833
}
28012834
CodeExpr::Defer(DeferExpr { expr, loc: _ }) => {
@@ -3638,7 +3671,8 @@ impl Compiler {
36383671
| InfixOpTag::Assign
36393672
| InfixOpTag::FieldAccess
36403673
| InfixOpTag::Catch
3641-
| InfixOpTag::ErrorPropagation => unreachable!(),
3674+
| InfixOpTag::ErrorPropagation
3675+
| InfixOpTag::ExprPipe => unreachable!(),
36423676
},
36433677
CodeExpr::PrefixOp(PrefixOpExpr {
36443678
op_tag,
@@ -3812,6 +3846,39 @@ impl Compiler {
38123846
let result = self.assert_catchable_type(&expr_type, loc)?;
38133847
Ok(result.ok.as_ref().clone())
38143848
}
3849+
CodeExpr::ExprPipe(ExprPipeExpr {
3850+
lhs,
3851+
rhs,
3852+
op_loc,
3853+
loc: _,
3854+
}) => {
3855+
let ctx = ctx.be_mut();
3856+
3857+
let lhs_type = catch!(self.get_expr_type(ctx, &lhs), err, {
3858+
self.report_error(&err);
3859+
return Ok(LoType::Never);
3860+
});
3861+
3862+
ctx.enter_scope(LoScopeType::Block);
3863+
3864+
ctx.current_scope_mut().macro_args.push(LoConstDef {
3865+
const_name: String::from("it"),
3866+
code_unit: LoCodeUnit {
3867+
type_: lhs_type,
3868+
instrs: Vec::new(),
3869+
},
3870+
loc: op_loc.clone(),
3871+
});
3872+
3873+
let rhs_type = catch!(self.get_expr_type(ctx, &rhs), err, {
3874+
self.report_error(&err);
3875+
return Ok(LoType::Never);
3876+
});
3877+
3878+
ctx.exit_scope();
3879+
3880+
return Ok(rhs_type);
3881+
}
38153882
CodeExpr::Dbg(_) => Ok(LoType::StructInstance {
38163883
struct_index: self.modules[ctx.module_index]
38173884
.get_item(STR_TYPE_NAME)
@@ -5085,7 +5152,8 @@ impl Compiler {
50855152
| InfixOpTag::Assign
50865153
| InfixOpTag::FieldAccess
50875154
| InfixOpTag::Catch
5088-
| InfixOpTag::ErrorPropagation => unreachable!(),
5155+
| InfixOpTag::ErrorPropagation
5156+
| InfixOpTag::ExprPipe => unreachable!(),
50895157
}
50905158

50915159
let module = self.get_module_by_file_index(op_loc.file_index).unwrap();
@@ -5132,7 +5200,8 @@ impl Compiler {
51325200
| InfixOpTag::Assign
51335201
| InfixOpTag::FieldAccess
51345202
| InfixOpTag::Catch
5135-
| InfixOpTag::ErrorPropagation => None,
5203+
| InfixOpTag::ErrorPropagation
5204+
| InfixOpTag::ExprPipe => None,
51365205
}
51375206
}
51385207

src/lexer.rs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ static OPERATORS: &[&str] = &[
527527
"^=", // Bitwise exclusive OR and assignment
528528
"|", // Bitwise OR
529529
"|=", // Bitwise OR and assignment
530+
"|>", // Expression piping
530531
".", // Member access
531532
"..", // Range operator
532533
":", // Type separator
@@ -581,6 +582,8 @@ pub enum InfixOpTag {
581582
Catch,
582583

583584
ErrorPropagation,
585+
586+
ExprPipe,
584587
}
585588

586589
pub struct InfixOp {
@@ -600,37 +603,39 @@ impl InfixOp {
600603
}
601604

602605
match token.get_value(source) {
603-
"catch" => op(token, Catch, 13, L),
606+
"catch" => op(token, Catch, 14, Left),
607+
608+
"." => op(token, FieldAccess, 13, Left),
604609

605-
"." => op(token, FieldAccess, 12, L),
610+
"?" => op(token, ErrorPropagation, 12, None),
606611

607-
"?" => op(token, ErrorPropagation, 11, None),
612+
"as" => op(token, Cast, 11, Left),
608613

609-
"as" => op(token, Cast, 10, L),
614+
"%" => op(token, Mod, 10, Left),
615+
"/" => op(token, Div, 10, Left),
616+
"*" => op(token, Mul, 10, Left),
610617

611-
"%" => op(token, Mod, 9, L),
612-
"/" => op(token, Div, 9, L),
613-
"*" => op(token, Mul, 9, L),
618+
"-" => op(token, Sub, 9, Left),
619+
"+" => op(token, Add, 9, Left),
614620

615-
"-" => op(token, Sub, 8, L),
616-
"+" => op(token, Add, 8, L),
621+
">>" => op(token, ShiftRight, 8, Left),
622+
"<<" => op(token, ShiftLeft, 8, Left),
617623

618-
">>" => op(token, ShiftRight, 7, L),
619-
"<<" => op(token, ShiftLeft, 7, L),
624+
"&" => op(token, BitAnd, 7, Left),
620625

621-
"&" => op(token, BitAnd, 6, L),
626+
"|" => op(token, BitOr, 6, Left),
622627

623-
"|" => op(token, BitOr, 5, L),
628+
">=" => op(token, GreaterEqual, 5, Left),
629+
">" => op(token, Greater, 5, Left),
630+
"<=" => op(token, LessEqual, 5, Left),
631+
"<" => op(token, Less, 5, Left),
632+
"!=" => op(token, NotEqual, 5, None),
633+
"==" => op(token, Equal, 5, None),
624634

625-
">=" => op(token, GreaterEqual, 4, L),
626-
">" => op(token, Greater, 4, L),
627-
"<=" => op(token, LessEqual, 4, L),
628-
"<" => op(token, Less, 4, L),
629-
"!=" => op(token, NotEqual, 4, None),
630-
"==" => op(token, Equal, 4, None),
635+
"&&" => op(token, And, 4, Left),
636+
"||" => op(token, Or, 3, Left),
631637

632-
"&&" => op(token, And, 3, L),
633-
"||" => op(token, Or, 2, L),
638+
"|>" => op(token, ExprPipe, 2, Left),
634639

635640
"=" => op(token, Assign, 1, None),
636641
"+=" => op(token, AddAssign, 1, None),
@@ -666,10 +671,10 @@ impl PrefixOp {
666671
use PrefixOpTag::*;
667672

668673
let (tag, info) = match token.get_value(source) {
669-
"!" => (Not, OpInfo { bp: 8, assoc: L }),
670-
"*" => (Dereference, OpInfo { bp: 8, assoc: L }),
671-
"+" => (Positive, OpInfo { bp: 9, assoc: L }),
672-
"-" => (Negative, OpInfo { bp: 9, assoc: L }),
674+
"!" => (Not, OpInfo { bp: 8, assoc: Left }),
675+
"*" => (Dereference, OpInfo { bp: 8, assoc: Left }),
676+
"+" => (Positive, OpInfo { bp: 9, assoc: Left }),
677+
"-" => (Negative, OpInfo { bp: 9, assoc: Left }),
673678
_ => return Option::None,
674679
};
675680
Some(Self { tag, info, token })
@@ -678,7 +683,7 @@ impl PrefixOp {
678683

679684
#[derive(PartialEq)]
680685
pub enum OpAssoc {
681-
L,
686+
Left,
682687
None,
683688
}
684689

@@ -689,7 +694,7 @@ pub struct OpInfo {
689694

690695
impl OpInfo {
691696
pub fn get_min_bp_for_next(&self) -> u32 {
692-
if self.assoc == OpAssoc::L {
697+
if self.assoc == OpAssoc::Left {
693698
self.bp + 1
694699
} else {
695700
self.bp

src/parser.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,20 @@ impl Parser {
13281328
loc,
13291329
}))
13301330
}
1331+
InfixOpTag::ExprPipe => {
1332+
let mut loc = primary.loc().clone();
1333+
1334+
let rhs = self.parse_code_expr(min_bp)?;
1335+
1336+
loc.end_pos = self.prev().loc.end_pos;
1337+
1338+
Ok(CodeExpr::ExprPipe(ExprPipeExpr {
1339+
lhs: Box::new(primary),
1340+
rhs: Box::new(rhs),
1341+
op_loc: op.token.loc,
1342+
loc,
1343+
}))
1344+
}
13311345
}
13321346
}
13331347

src/printer.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,20 @@ impl Printer {
578578
stdout_write(" ");
579579
self.print_code_expr(rhs);
580580
}
581+
CodeExpr::ExprPipe(ExprPipeExpr {
582+
lhs,
583+
rhs,
584+
op_loc,
585+
loc: _,
586+
}) => {
587+
self.print_code_expr(lhs);
588+
if !self.print_backslashes_before(op_loc.pos.offset) {
589+
stdout_write(" ");
590+
}
591+
stdout_write(op_loc.read_span(self.parser.lexer.source));
592+
stdout_write(" ");
593+
self.print_code_expr(rhs);
594+
}
581595
CodeExpr::PrefixOp(PrefixOpExpr {
582596
expr,
583597
op_tag: _,

vscode-ext/lo.vsix

3.83 KB
Binary file not shown.

0 commit comments

Comments
 (0)