Generate placeholder code for "mark in.dst".
diff --git a/cmd/puffs-c/internal/cgen/statement.go b/cmd/puffs-c/internal/cgen/statement.go
index 4995671..09118fd 100644
--- a/cmd/puffs-c/internal/cgen/statement.go
+++ b/cmd/puffs-c/internal/cgen/statement.go
@@ -68,6 +68,10 @@
b.writes(";\n")
return nil
+ case a.KIO:
+ b.writes("/* placeholder */\n")
+ return nil
+
case a.KIf:
// TODO: for writeSuspendibles, make sure that we get order of
// sub-expression evaluation correct.
diff --git a/doc/puffs-the-language.md b/doc/puffs-the-language.md
index 01654a8..6a3975f 100644
--- a/doc/puffs-the-language.md
+++ b/doc/puffs-the-language.md
@@ -54,9 +54,10 @@
- `var`
-1 keyword deals with I/O:
+2 keywords deal with I/O:
- `limit`
+- `mark`
TODO: categorize and, or, not, as, ref, deref, false, true, in, out, this, u8,
u16, etc.
diff --git a/gen/c/std/flate.c b/gen/c/std/flate.c
index 696e280..457ccc4 100644
--- a/gen/c/std/flate.c
+++ b/gen/c/std/flate.c
@@ -831,6 +831,7 @@
switch (coro_susp_point) {
PUFFS_BASE__COROUTINE_SUSPENSION_POINT(0);
+ /* placeholder */
while (true) {
PUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
if (a_dst.buf) {
diff --git a/lang/ast/ast.go b/lang/ast/ast.go
index 7146313..33ab789 100644
--- a/lang/ast/ast.go
+++ b/lang/ast/ast.go
@@ -40,6 +40,7 @@
KField
KFile
KFunc
+ KIO
KIf
KIterate
KJump
@@ -71,6 +72,7 @@
KField: "KField",
KFile: "KFile",
KFunc: "KFunc",
+ KIO: "KIO",
KIf: "KIf",
KIterate: "KIterate",
KJump: "KJump",
@@ -133,6 +135,7 @@
func (n *Node) Field() *Field { return (*Field)(n) }
func (n *Node) File() *File { return (*File)(n) }
func (n *Node) Func() *Func { return (*Func)(n) }
+func (n *Node) IO() *IO { return (*IO)(n) }
func (n *Node) If() *If { return (*If)(n) }
func (n *Node) Iterate() *Iterate { return (*Iterate)(n) }
func (n *Node) Jump() *Jump { return (*Jump)(n) }
@@ -480,6 +483,27 @@
}
}
+// IO is "mark LHS":
+// - ID0: <IDMark>
+// - LHS: <Expr>
+//
+// TODO: "mark LHS { List2 }"?
+//
+// TODO: also represent "limit LHS"?
+type IO Node
+
+func (n *IO) Node() *Node { return (*Node)(n) }
+func (n *IO) Keyword() t.ID { return n.id0 }
+func (n *IO) Value() *Expr { return n.lhs.Expr() }
+
+func NewIO(keyword t.ID, value *Expr) *IO {
+ return &IO{
+ kind: KIO,
+ id0: keyword,
+ lhs: value.Node(),
+ }
+}
+
// Return is "return LHS", "return error ID1" or "return suspension ID1":
// - ID0: <0|IDError|IDSuspension>
// - ID1: message
diff --git a/lang/check/bounds.go b/lang/check/bounds.go
index 18ff1f3..c517f2d 100644
--- a/lang/check/bounds.go
+++ b/lang/check/bounds.go
@@ -241,6 +241,10 @@
_, _, err := q.bcheckExpr(n.Expr(), 0)
return err
+ case a.KIO:
+ _, _, err := q.bcheckExpr(n.IO().Value(), 0)
+ return err
+
case a.KIf:
return q.bcheckIf(n.If())
diff --git a/lang/check/type.go b/lang/check/type.go
index 38ee599..df4bb09 100644
--- a/lang/check/type.go
+++ b/lang/check/type.go
@@ -84,6 +84,18 @@
case a.KExpr:
return q.tcheckExpr(n.Expr(), 0)
+ case a.KIO:
+ n := n.IO()
+ val := n.Value()
+ if err := q.tcheckExpr(val, 0); err != nil {
+ return err
+ }
+ typ := val.MType()
+ if key := typ.Name().Key(); typ.Decorator() != 0 || (key != t.KeyReader1 && key != t.KeyWriter1) {
+ return fmt.Errorf("check: %s expression %q, of type %q, does not have an I/O type",
+ n.Keyword().String(q.tm), val.String(q.tm), typ.String(q.tm))
+ }
+
case a.KIf:
for n := n.If(); n != nil; n = n.ElseIf() {
cond := n.Condition()
diff --git a/lang/parse/parse.go b/lang/parse/parse.go
index ab0ce69..ef29acf 100644
--- a/lang/parse/parse.go
+++ b/lang/parse/parse.go
@@ -632,6 +632,14 @@
o, err := p.parseIf()
return o.Node(), err
+ case t.KeyMark:
+ p.src = p.src[1:]
+ value, err := p.parseExpr()
+ if err != nil {
+ return nil, err
+ }
+ return a.NewIO(x, value).Node(), nil
+
case t.KeyReturn:
p.src = p.src[1:]
keyword, message, value, err := t.ID(0), t.ID(0), (*a.Expr)(nil), error(nil)
diff --git a/lang/token/list.go b/lang/token/list.go
index a11499d..a6c90fc 100644
--- a/lang/token/list.go
+++ b/lang/token/list.go
@@ -242,6 +242,7 @@
KeyConst = Key(IDConst >> KeyShift)
KeyTry = Key(IDTry >> KeyShift)
KeyIterate = Key(IDIterate >> KeyShift)
+ KeyMark = Key(IDMark >> KeyShift)
KeyFalse = Key(IDFalse >> KeyShift)
KeyTrue = Key(IDTrue >> KeyShift)
@@ -428,6 +429,7 @@
IDConst = ID(0x77<<KeyShift | FlagsOther)
IDTry = ID(0x78<<KeyShift | FlagsOther)
IDIterate = ID(0x79<<KeyShift | FlagsOther)
+ IDMark = ID(0x7A<<KeyShift | FlagsOther)
IDFalse = ID(0x80<<KeyShift | FlagsLiteral | FlagsImplicitSemicolon)
IDTrue = ID(0x81<<KeyShift | FlagsLiteral | FlagsImplicitSemicolon)
@@ -630,6 +632,7 @@
KeyConst: {"const", IDConst},
KeyTry: {"try", IDTry},
KeyIterate: {"iterate", IDIterate},
+ KeyMark: {"mark", IDMark},
KeyFalse: {"false", IDFalse},
KeyTrue: {"true", IDTrue},
diff --git a/std/flate/decode_flate.puffs b/std/flate/decode_flate.puffs
index 2aaec98..427f3f5 100644
--- a/std/flate/decode_flate.puffs
+++ b/std/flate/decode_flate.puffs
@@ -116,6 +116,11 @@
)
pub func flate_decoder.decode?(dst writer1, src reader1)() {
+ // TODO: when is the mark undone? Explicitly or implicitly? Right now, we
+ // just assume that the mark is dropped at function end, since the arg is a
+ // writer1 not a *writer1. Should we support nested marks? Should this be
+ // "mark dst { etc }" that has a block and unmarks at the block end?
+ mark in.dst
while true {
var z status = try this.decode_blocks?(dst:in.dst, src:in.src)
if not z.is_suspension() {