Skip to content

Commit ed9126e

Browse files
committed
Implement iteration on constant integers & strings
1 parent b32fb86 commit ed9126e

File tree

8 files changed

+320
-53
lines changed

8 files changed

+320
-53
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
members = [
44
"script",
55
]
6+
7+
[profile.release]
8+
lto = true
9+
codegen-units = 1

script/examples/count.bs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main()
2+
for i in 100_000_000
3+
pass
File renamed without changes.

script/src/ast.rs

Lines changed: 153 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::tokenizer::*;
2+
use core::str::FromStr;
23

34
type Integer = isize;
45
type Real = f64;
@@ -32,6 +33,11 @@ pub(crate) enum Statement<'src> {
3233
func: &'src str,
3334
args: Vec<Expression<'src>>,
3435
},
36+
For {
37+
var: &'src str,
38+
expr: Expression<'src>,
39+
lines: Lines<'src>,
40+
},
3541
}
3642

3743
#[derive(Debug)]
@@ -71,6 +77,7 @@ pub enum ErrorType {
7177
UnexpectedEOL,
7278
UnexpectedSpace,
7379
Noop,
80+
NotANumber,
7481
}
7582

7683
macro_rules! err {
@@ -134,10 +141,10 @@ impl<'src> Script<'src> {
134141
}
135142
}
136143

144+
type TokenGroup<'src> = (Token<'src>, usize, usize);
145+
137146
impl<'src> Function<'src> {
138-
fn parse(
139-
tokens: &mut impl Iterator<Item = (Token<'src>, usize, usize)>,
140-
) -> Result<Self, Error> {
147+
fn parse(tokens: &mut impl Iterator<Item = TokenGroup<'src>>) -> Result<Self, Error> {
141148
let name = match skip_whitespace(tokens) {
142149
Some((Token::Name(name), _, _)) => name,
143150
Some((_, l, c)) => err!(UnexpectedToken, l, c),
@@ -162,27 +169,46 @@ impl<'src> Function<'src> {
162169
Some((_, l, c)) => err!(UnexpectedToken, l, c),
163170
None => err!(UnexpectedEOL, 0, 0),
164171
}
165-
let mut tab_count = 0;
166-
let (mut tk, mut line, mut column) = loop {
167-
match tokens.next() {
168-
Some((Token::Tab, _, _)) => tab_count += 1,
169-
Some(e) => break e,
170-
None => err!(UnexpectedEOL, 0, 0),
171-
}
172+
173+
// Ensure there is one and only one tab
174+
let (l, c) = match tokens.next() {
175+
Some((Token::Tab, l, c)) => (l, c),
176+
Some((e, l, c)) => err!(UnexpectedToken, l, c),
177+
None => err!(UnexpectedEOL, 0, 0),
172178
};
173-
let tab_count = tab_count;
174179

180+
Ok(Self {
181+
name,
182+
parameters,
183+
lines: Self::parse_block(tokens, 1, l, c)?.0,
184+
})
185+
}
186+
187+
fn parse_block(
188+
tokens: &mut impl Iterator<Item = TokenGroup<'src>>,
189+
expected_indent: u8,
190+
mut line: usize,
191+
mut column: usize,
192+
) -> Result<(Lines<'src>, u8), Error> {
175193
let mut lines = Lines::new();
176-
let mut curr_tabs = 0;
177194
loop {
178-
match tk {
179-
Token::Space => err!(UnexpectedSpace, line, column),
180-
Token::Tab => curr_tabs += 1,
181-
Token::EOL => curr_tabs = 0,
182-
Token::Name(name) => {
195+
match tokens.next() {
196+
Some((Token::EOL, ..)) => {
197+
for i in 0..expected_indent {
198+
if let Some((tk, l, c)) = tokens.next() {
199+
if tk == Token::Space {
200+
err!(UnexpectedToken, l, c);
201+
} else if tk != Token::Tab {
202+
return Ok((lines, i));
203+
}
204+
}
205+
}
206+
}
207+
Some((Token::Tab, l, c)) => err!(UnexpectedToken, l, c),
208+
Some((Token::Name(name), ll, lc)) => {
183209
let mut args = Vec::new();
184210
match skip_whitespace(tokens) {
185-
Some((Token::EOL, ..)) => break,
211+
Some((Token::EOL, ..)) => continue,
186212
Some((Token::BracketRoundOpen, l, c)) => match skip_whitespace(tokens) {
187213
Some((Token::BracketRoundClose, ..)) => (),
188214
Some((pre, ..)) => {
@@ -205,18 +231,61 @@ impl<'src> Function<'src> {
205231
dbg!(e);
206232
todo!()
207233
}
208-
None => err!(UnexpectedEOL, 0, 0),
234+
None => err!(UnexpectedEOL, ll, lc),
209235
}
210236
}
211-
_ => todo!(),
212-
}
237+
Some((Token::For, mut ll, mut lc)) => {
238+
let var = match skip_whitespace(tokens) {
239+
Some((Token::Name(n), ..)) => n,
240+
Some((_, l, c)) => err!(UnexpectedToken, l, c),
241+
None => err!(UnexpectedEOL, line, column),
242+
};
243+
match skip_whitespace(tokens) {
244+
Some((Token::In, ..)) => (),
245+
Some((_, l, c)) => err!(UnexpectedToken, l, c),
246+
None => err!(UnexpectedEOL, line, column),
247+
}
248+
let (expr, tk) = match skip_whitespace(tokens) {
249+
Some((tk, ..)) => Expression::parse(tk, tokens)?,
250+
None => err!(UnexpectedEOL, line, column),
251+
};
252+
if tk == Token::EOL {
253+
let expected_indent = expected_indent + 1;
254+
'eol: loop {
255+
for i in 0..expected_indent {
256+
match tokens.next() {
257+
Some((Token::Tab, l, c)) => {
258+
ll = l;
259+
lc = c;
260+
}
261+
Some((Token::EOL, l, c)) => {
262+
ll = l;
263+
lc = c;
264+
continue 'eol;
265+
}
266+
Some((_, l, c)) => err!(UnexpectedToken, l, c),
267+
None => err!(UnexpectedEOL, ll, lc),
268+
}
269+
}
270+
break;
271+
}
272+
lines.push(Statement::For {
273+
var,
274+
expr,
275+
lines: Self::parse_block(tokens, expected_indent, ll, lc)?.0,
276+
});
277+
} else {
278+
err!(UnexpectedToken, 0, 0);
279+
}
280+
}
281+
Some((Token::Pass, ..)) => (),
282+
Some((tk, ..)) => {
283+
dbg!(tk);
284+
todo!()
285+
}
286+
None => return Ok((lines, 0)),
287+
};
213288
}
214-
215-
Ok(Self {
216-
name,
217-
parameters,
218-
lines,
219-
})
220289
}
221290
}
222291

@@ -231,6 +300,17 @@ impl<'src> Expression<'src> {
231300
None => err!(UnexpectedEOL, 0, 0),
232301
},
233302
Token::String(s) => (Expression::Atom(Atom::String(s)), None),
303+
Token::Number(n) => (
304+
Expression::Atom(if let Ok(n) = parse_integer(n) {
305+
Atom::Integer(n)
306+
} else if let Ok(n) = Real::from_str(n) {
307+
Atom::Real(n)
308+
} else {
309+
dbg!(n);
310+
err!(NotANumber, 0, 0);
311+
}),
312+
None,
313+
),
234314
e => {
235315
dbg!(e);
236316
todo!()
@@ -239,7 +319,9 @@ impl<'src> Expression<'src> {
239319
match skip_whitespace(tokens) {
240320
Some((Token::BracketRoundClose, ..)) => return Ok((lhs, Token::BracketRoundClose)),
241321
Some((Token::Comma, ..)) => return Ok((lhs, Token::Comma)),
242-
_ => todo!(),
322+
Some((Token::EOL, ..)) => return Ok((lhs, Token::EOL)),
323+
Some((tk, ..)) => todo!("{:?}", tk),
324+
None => todo!("none"),
243325
}
244326
/*
245327
let lhs = match skip_whitespace(tokens) {
@@ -261,3 +343,46 @@ impl Error {
261343
})
262344
}
263345
}
346+
347+
enum NumberParseError {
348+
InvalidBase,
349+
InvalidDigit,
350+
Empty,
351+
}
352+
353+
/// Custom integer parsing function that allows underscores
354+
fn parse_integer(s: &str) -> Result<Integer, NumberParseError> {
355+
let mut chars = s.chars();
356+
let (mut chars, base) = if chars.next() == Some('0') {
357+
if let Some(c) = chars.next() {
358+
let b = match c {
359+
'x' => 16,
360+
'b' => 2,
361+
'o' => 8,
362+
_ => return Err(NumberParseError::InvalidBase),
363+
};
364+
(chars, b)
365+
} else {
366+
return Ok(0);
367+
}
368+
} else {
369+
(s.chars(), 10)
370+
};
371+
if s == "" {
372+
Err(NumberParseError::Empty)
373+
} else {
374+
let mut chars = chars.peekable();
375+
let neg = if chars.peek() == Some(&'-') {
376+
chars.next();
377+
true
378+
} else {
379+
false
380+
};
381+
let mut n = 0;
382+
for c in chars.filter(|&c| c != '_') {
383+
n *= base as Integer;
384+
n += c.to_digit(base).ok_or(NumberParseError::InvalidDigit)? as isize;
385+
}
386+
Ok(if neg { -n } else { n })
387+
}
388+
}

0 commit comments

Comments
 (0)