Skip to content

Commit 0f68a86

Browse files
committed
Add for and for each loop
1 parent 9e4f867 commit 0f68a86

File tree

14 files changed

+179
-5
lines changed

14 files changed

+179
-5
lines changed

docs/assets/for.png

37.4 KB
Loading

docs/assets/foreach.png

11.9 KB
Loading

docs/language/control-flow.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ until condition == true {
2020

2121
![](../assets/until.png){width="200"}
2222

23+
## for each loop
24+
25+
```goboscript
26+
for x in n {
27+
# code
28+
}
29+
```
30+
31+
![](../assets/foreach.png){width="200"}
32+
33+
## for loop
34+
35+
```goboscript
36+
for x = 0; x > n; x++ {
37+
# code
38+
}
39+
```
40+
41+
![](../assets/for.png){width="100"}
42+
2343
## forever loop
2444

2545
```goboscript

editors/code/syntaxes/goboscript.tmGrammar.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ patterns:
3131
- name: keyword
3232
match: "\\b(costumes|sounds|global|list|nowarp|onflag|onkey|onbackdrop|onloudness|ontimer|on|onclone)\\b"
3333
- name: keyword.control
34-
match: "\\b(if|else|elif|until|forever|repeat|delete|at|add|to|insert|true|false|as|struct|enum|return)\\b"
34+
match: "\\b(if|else|elif|until|for|forever|repeat|delete|at|add|to|insert|true|false|as|struct|enum|return)\\b"
3535
- name: keyword
3636
match: "\\b(error|warn|breakpoint|local|not|and|or|in|length|round|abs|floor|ceil|sqrt|sin|cos|tan|asin|acos|atan|ln|log|antiln|antilog)\\b"
3737
- name: support.function.builtin

editors/notepad++/goboscript.udl.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<Keywords name="Folders in comment, open"></Keywords>
2525
<Keywords name="Folders in comment, middle"></Keywords>
2626
<Keywords name="Folders in comment, close"></Keywords>
27-
<Keywords name="Keywords1">costumes sounds local proc func return nowarp if else elif until forever repeat list cloud struct enum</Keywords>
27+
<Keywords name="Keywords1">costumes sounds local proc func return nowarp if else elif until for forever repeat list cloud struct enum</Keywords>
2828
<Keywords name="Keywords2">%define&#x000D;&#x000A;%if&#x000D;&#x000A;%else&#x000D;&#x000A;%endif&#x000D;&#x000A;%include&#x000D;&#x000A;%undef</Keywords>
2929
<Keywords name="Keywords3">true false</Keywords>
3030
<Keywords name="Keywords4">$</Keywords>

editors/sublime/goboscript.sublime-syntax

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ contexts:
2222
match: "\\b(costumes|sounds|global|variables|lists|nowarp|onflag|onkey|onbackdrop|onloudness|ontimer|on|onclone)\\b"
2323

2424
- scope: keyword.control
25-
match: "\\b(if|else|elif|until|forever|repeat|delete|at|add|to|insert)\\b"
25+
match: "\\b(if|else|elif|until|for|forever|repeat|delete|at|add|to|insert)\\b"
2626

2727
- scope: keyword
2828
match: "\\b(error|warn|breakpoint|local|not|and|or|in|length|round|abs|floor|ceil|sqrt|sin|cos|tan|asin|acos|atan|ln|log|antiln|antilog)\\b"

src/ast/stmt.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ pub enum Stmt {
2020
times: Box<Expr>,
2121
body: Vec<Stmt>,
2222
},
23+
ForEach {
24+
name: Name,
25+
times: Box<Expr>,
26+
body: Vec<Stmt>,
27+
},
28+
For {
29+
name: Name,
30+
value: Box<Expr>,
31+
type_: Type,
32+
cond: Box<Expr>,
33+
incr: Box<Stmt>,
34+
body: Vec<Stmt>,
35+
},
2336
Forever {
2437
body: Vec<Stmt>,
2538
span: Span,

src/codegen/sb3.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ impl Stmt {
224224
fn opcode(&self, s: S) -> &'static str {
225225
match self {
226226
Stmt::Repeat { .. } => "control_repeat",
227+
Stmt::ForEach { .. } => "control_for_each",
228+
Stmt::For { .. } => "control_repeat_until",
227229
Stmt::Forever { .. } => "control_forever",
228230
Stmt::Branch { else_body, .. } => {
229231
if else_body.is_empty() {
@@ -965,18 +967,20 @@ where T: Write + Seek
965967
self.stmts(s, d, &event.body, next_id, Some(this_id))
966968
}
967969

968-
pub fn stmts(
970+
pub fn stmts_with_next(
969971
&mut self,
970972
s: S,
971973
d: D,
972974
stmts: &[Stmt],
973975
mut this_id: NodeID,
976+
last_id: Option<NodeID>,
974977
mut parent_id: Option<NodeID>,
975978
) -> io::Result<()> {
976979
for (i, stmt) in stmts.iter().enumerate() {
977980
let is_last = i == stmts.len() - 1;
978981
if is_last || stmt.is_terminator() {
979-
self.stmt(s, d, stmt, this_id, None, parent_id)?;
982+
let next_id = last_id;
983+
self.stmt(s, d, stmt, this_id, next_id, parent_id)?;
980984
if !is_last {
981985
d.report(DiagnosticKind::FollowedByUnreachableCode, stmt.span());
982986
}
@@ -990,6 +994,17 @@ where T: Write + Seek
990994
Ok(())
991995
}
992996

997+
pub fn stmts(
998+
&mut self,
999+
s: S,
1000+
d: D,
1001+
stmts: &[Stmt],
1002+
this_id: NodeID,
1003+
parent_id: Option<NodeID>,
1004+
) -> io::Result<()> {
1005+
self.stmts_with_next(s, d, stmts, this_id, None, parent_id)
1006+
}
1007+
9931008
pub fn stmt(
9941009
&mut self,
9951010
s: S,
@@ -1006,6 +1021,13 @@ where T: Write + Seek
10061021
)?;
10071022
match stmt {
10081023
Stmt::Repeat { times, body } => self.repeat(s, d, this_id, times, body),
1024+
Stmt::ForEach { name, times, body } => self.foreach(s, d, this_id, name, times, body),
1025+
Stmt::For {
1026+
cond,
1027+
incr,
1028+
body,
1029+
..
1030+
} => self.r#for(s, d, this_id, cond, incr, body),
10091031
Stmt::Forever { body, span } => self.forever(s, d, this_id, body, span),
10101032
Stmt::Branch {
10111033
cond,

src/codegen/stmt.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,60 @@ where T: Write + Seek
5555
self.stmts(s, d, body, body_id, Some(this_id))
5656
}
5757

58+
pub fn foreach(
59+
&mut self,
60+
s: S,
61+
d: D,
62+
this_id: NodeID,
63+
name: &Name,
64+
times: &Expr,
65+
body: &[Stmt],
66+
) -> io::Result<()> {
67+
let times_id = self.id.new_id();
68+
let body_id = self.id.new_id();
69+
self.begin_inputs()?;
70+
self.input(s, d, "VALUE", times, times_id)?;
71+
self.substack("SUBSTACK", (!body.is_empty()).then_some(body_id))?;
72+
self.end_obj()?; // inputs
73+
match s.qualify_name(d, name) {
74+
Some(QualifiedName::Var(qualified_name, _)) => {
75+
self.single_field_id("VARIABLE", &qualified_name)?
76+
}
77+
Some(QualifiedName::List(..)) => {
78+
d.report(
79+
DiagnosticKind::UnrecognizedVariable(name.basename().clone()),
80+
&name.span(),
81+
);
82+
}
83+
None => {}
84+
}
85+
self.end_obj()?; // node
86+
self.expr(s, d, times, times_id, this_id)?;
87+
self.stmts(s, d, body, body_id, Some(this_id))
88+
}
89+
90+
pub fn r#for(
91+
&mut self,
92+
s: S,
93+
d: D,
94+
this_id: NodeID,
95+
cond: &Expr,
96+
incr: &Stmt,
97+
body: &[Stmt],
98+
) -> io::Result<()> {
99+
let cond_id = self.id.new_id();
100+
let incr_id = self.id.new_id();
101+
let body_id = self.id.new_id();
102+
self.begin_inputs()?;
103+
self.input(s, d, "CONDITION", cond, cond_id)?;
104+
self.substack("SUBSTACK", Some((!body.is_empty()).then_some(body_id).unwrap_or(incr_id)))?;
105+
self.end_obj()?; // inputs
106+
self.end_obj()?; // node
107+
self.expr(s, d, cond, cond_id, this_id)?;
108+
self.stmts_with_next(s, d, body, body_id, Some(incr_id), Some(this_id))?;
109+
self.stmt(s, d, incr, incr_id, None, Some(body_id))
110+
}
111+
58112
pub fn forever(
59113
&mut self,
60114
s: S,

src/lexer/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub enum Token {
7171
Elif,
7272
#[token("until")]
7373
Until,
74+
#[token("for")]
75+
For,
7476
#[token("forever")]
7577
Forever,
7678
#[token("repeat")]

0 commit comments

Comments
 (0)