Skip to content

Commit 9b2cf20

Browse files
committed
Implement fundamentals for expression parsing
1 parent ed9126e commit 9b2cf20

File tree

7 files changed

+619
-150
lines changed

7 files changed

+619
-150
lines changed

script/examples/vec2.bs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main()
2+
print(length(0.5, 0.7))
3+
4+
5+
fn length(x, y)
6+
#return (x * x + y * y).sqrt()
7+
return x * x + y * y

script/src/ast.rs

Lines changed: 195 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ pub(crate) enum Statement<'src> {
3838
expr: Expression<'src>,
3939
lines: Lines<'src>,
4040
},
41+
Return {
42+
expr: Option<Expression<'src>>,
43+
},
4144
}
4245

43-
#[derive(Debug)]
46+
#[derive(Debug, PartialEq)]
4447
pub(crate) enum Atom<'src> {
4548
Name(&'src str),
4649
Real(Real),
@@ -156,11 +159,19 @@ impl<'src> Function<'src> {
156159
None => err!(UnexpectedEOL, 0, 0),
157160
}
158161

159-
let parameters = Vec::new();
162+
let mut parameters = Vec::new();
160163
loop {
161164
match skip_whitespace(tokens) {
162165
Some((Token::BracketRoundClose, _, _)) => break,
163-
_ => todo!(),
166+
Some((Token::Name(a), l, c)) => {
167+
parameters.push(a);
168+
match skip_whitespace(tokens) {
169+
Some((Token::BracketRoundClose, ..)) => break,
170+
Some((Token::Comma, ..)) => (),
171+
e => todo!("{:?}", e),
172+
}
173+
}
174+
e => todo!("{:?}", e),
164175
}
165176
}
166177

@@ -216,8 +227,8 @@ impl<'src> Function<'src> {
216227
let (expr, last_tk) = Expression::parse(pre, tokens)?;
217228
args.push(expr);
218229
match last_tk {
219-
Token::Comma => (),
220-
Token::BracketRoundClose => break,
230+
Some(Token::Comma) => (),
231+
Some(Token::BracketRoundClose) => break,
221232
tk => {
222233
panic!("Expression did not parse all tokens: {:?}", tk)
223234
}
@@ -249,7 +260,7 @@ impl<'src> Function<'src> {
249260
Some((tk, ..)) => Expression::parse(tk, tokens)?,
250261
None => err!(UnexpectedEOL, line, column),
251262
};
252-
if tk == Token::EOL {
263+
if tk == Some(Token::EOL) {
253264
let expected_indent = expected_indent + 1;
254265
'eol: loop {
255266
for i in 0..expected_indent {
@@ -279,10 +290,18 @@ impl<'src> Function<'src> {
279290
}
280291
}
281292
Some((Token::Pass, ..)) => (),
282-
Some((tk, ..)) => {
283-
dbg!(tk);
284-
todo!()
285-
}
293+
Some((Token::Return, l, c)) => {
294+
if let Some((tk, l, c)) = skip_whitespace(tokens) {
295+
let expr = match Expression::parse(tk, tokens) {
296+
Ok(expr) => Some(expr.0),
297+
e => todo!("{:?}", e),
298+
};
299+
lines.push(Statement::Return { expr });
300+
} else {
301+
err!(UnexpectedEOL, l, c);
302+
}
303+
}
304+
Some((tk, ..)) => todo!("{:?}", tk),
286305
None => return Ok((lines, 0)),
287306
};
288307
}
@@ -293,44 +312,108 @@ impl<'src> Expression<'src> {
293312
fn parse(
294313
pre: Token<'src>,
295314
tokens: &mut impl Iterator<Item = (Token<'src>, usize, usize)>,
296-
) -> Result<(Self, Token<'src>), Error> {
315+
) -> Result<(Self, Option<Token<'src>>), Error> {
297316
let (lhs, last_tk) = match pre {
298317
Token::BracketRoundOpen => match skip_whitespace(tokens) {
299-
Some((pre, ..)) => Self::parse(pre, tokens).map(|(e, t)| (e, Some(t)))?,
318+
Some((pre, ..)) => Self::parse(pre, tokens).map(|(e, t)| (e, t))?,
300319
None => err!(UnexpectedEOL, 0, 0),
301320
},
302321
Token::String(s) => (Expression::Atom(Atom::String(s)), None),
303322
Token::Number(n) => (
304323
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)
324+
n
308325
} else {
309-
dbg!(n);
310326
err!(NotANumber, 0, 0);
311327
}),
312328
None,
313329
),
314-
e => {
315-
dbg!(e);
316-
todo!()
317-
}
330+
Token::Name(name) => {
331+
match skip_whitespace(tokens) {
332+
Some((Token::BracketRoundOpen, ..)) => {
333+
let mut arguments = Vec::new();
334+
loop {
335+
match skip_whitespace(tokens) {
336+
Some((Token::Number(n), l, c)) => {
337+
if let Ok(n) = parse_integer(n) {
338+
arguments.push(Expression::Atom(n));
339+
} else {
340+
err!(NotANumber, l, c);
341+
}
342+
},
343+
e => todo!("{:?}", e),
344+
}
345+
match skip_whitespace(tokens) {
346+
Some((Token::Comma, ..)) => (),
347+
Some((Token::BracketRoundClose, ..)) => break,
348+
Some((_, l, c)) => err!(UnexpectedToken, l, c),
349+
None => err!(UnexpectedEOL, 0, 0),
350+
}
351+
}
352+
(Expression::Function { name, arguments }, None)
353+
}
354+
Some((tk, ..)) => (Expression::Atom(Atom::Name(name)), Some(tk)),
355+
e => todo!("{:?}", e),
356+
}
357+
}
358+
e => todo!("{:?}", e),
318359
};
319-
match skip_whitespace(tokens) {
320-
Some((Token::BracketRoundClose, ..)) => return Ok((lhs, Token::BracketRoundClose)),
321-
Some((Token::Comma, ..)) => return Ok((lhs, Token::Comma)),
322-
Some((Token::EOL, ..)) => return Ok((lhs, Token::EOL)),
323-
Some((tk, ..)) => todo!("{:?}", tk),
324-
None => todo!("none"),
325-
}
326-
/*
327-
let lhs = match skip_whitespace(tokens) {
328-
Some((Token::BracketRoundOpen, ..)) => Self::parse(tokens)?,
329-
Some((Token::String(s), ..)) => Expression::Atom(Atom::String(s)),
330-
Some(e) => { dbg!(e); todo!() },
331-
None => todo!(),
332-
};
333-
*/
360+
if let Some(last_tk) = last_tk {
361+
match last_tk {
362+
Token::Op(opl) => {
363+
match skip_whitespace(tokens) {
364+
Some((Token::Name(mid), ..)) => {
365+
let mid = Expression::Atom(Atom::Name(mid));
366+
match skip_whitespace(tokens) {
367+
Some((Token::Op(opr), ..)) => {
368+
match skip_whitespace(tokens) {
369+
Some((Token::Name(rhs), ..)) => {
370+
let (left, op, right) = if opl >= opr {
371+
let rhs = Expression::parse(Token::Name(rhs), tokens)?.0;
372+
let lhs = Expression::Operation {
373+
op: opl,
374+
left: Box::new(lhs),
375+
right: Box::new(mid),
376+
};
377+
(lhs, opr, rhs)
378+
} else {
379+
let rhs = Expression::Atom(Atom::Name(rhs));
380+
let rhs = Expression::Operation {
381+
op: opr,
382+
left: Box::new(mid),
383+
right: Box::new(rhs),
384+
};
385+
(lhs, opl, rhs)
386+
};
387+
let (left, right) = (Box::new(left), Box::new(right));
388+
Ok((Expression::Operation { left, op, right }, skip_whitespace(tokens).map(|v| v.0)))
389+
}
390+
e => todo!("{:?}", e),
391+
}
392+
}
393+
Some((tk, ..)) if tk == Token::BracketRoundClose || tk == Token::EOL => {
394+
Ok((Expression::Operation {
395+
left: Box::new(lhs),
396+
op: opl,
397+
right: Box::new(mid),
398+
}, Some(tk)))
399+
}
400+
e => todo!("{:?}", e),
401+
}
402+
}
403+
e => todo!("{:?}", e),
404+
}
405+
}
406+
e => todo!("{:?}", e),
407+
}
408+
} else {
409+
match skip_whitespace(tokens) {
410+
Some((Token::BracketRoundClose, ..)) => return Ok((lhs, Some(Token::BracketRoundClose))),
411+
Some((Token::Comma, ..)) => return Ok((lhs, Some(Token::Comma))),
412+
Some((Token::EOL, ..)) => return Ok((lhs, Some(Token::EOL))),
413+
Some((tk, ..)) => todo!("{:?}", tk),
414+
None => todo!("none"),
415+
}
416+
}
334417
}
335418
}
336419

@@ -344,45 +427,100 @@ impl Error {
344427
}
345428
}
346429

430+
#[derive(Debug, PartialEq)]
347431
enum NumberParseError {
348432
InvalidBase,
349433
InvalidDigit,
350434
Empty,
435+
SeparatorInWrongPosition,
351436
}
352437

353-
/// Custom integer parsing function that allows underscores
354-
fn parse_integer(s: &str) -> Result<Integer, NumberParseError> {
438+
/// Custom number parsing function that allows underscores
439+
fn parse_integer(s: &str) -> Result<Atom, NumberParseError> {
355440
let mut chars = s.chars();
356441
let (mut chars, base) = if chars.next() == Some('0') {
357442
if let Some(c) = chars.next() {
358-
let b = match c {
359-
'x' => 16,
360-
'b' => 2,
361-
'o' => 8,
443+
if let Some(b) = match c {
444+
'x' => Some(16),
445+
'b' => Some(2),
446+
'o' => Some(8),
447+
'0' | '.' => None,
362448
_ => return Err(NumberParseError::InvalidBase),
363-
};
364-
(chars, b)
449+
} {
450+
(chars, b)
451+
} else {
452+
(s.chars(), 10)
453+
}
365454
} else {
366-
return Ok(0);
455+
return Ok(Atom::Integer(0));
367456
}
368457
} else {
369458
(s.chars(), 10)
370459
};
371460
if s == "" {
372461
Err(NumberParseError::Empty)
373462
} 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 })
463+
let mut chars = chars.peekable();
464+
let neg = if chars.peek() == Some(&'-') {
465+
chars.next();
466+
true
467+
} else {
468+
false
469+
};
470+
let mut chars = chars.filter(|&c| c != '_').peekable();
471+
// Real numbers and integers have to be processed separately as the range of a real can
472+
// exceed that of an integer
473+
if s.contains('.') {
474+
// Don't accept '.0', '0.' or even '.'. While many languages accept the former two,
475+
// I believe they are a poor choice for readability, hence they are disallowed.
476+
if chars.peek().unwrap() == &'.' {
477+
return Err(NumberParseError::SeparatorInWrongPosition);
478+
}
479+
let mut n = 0.0;
480+
loop {
481+
let c = chars.next().unwrap();
482+
if c == '.' {
483+
break;
484+
}
485+
n *= base as Real;
486+
n += c.to_digit(base).ok_or(NumberParseError::InvalidDigit)? as Real;
487+
}
488+
if chars.peek() == None {
489+
return Err(NumberParseError::SeparatorInWrongPosition);
490+
}
491+
let mut i = 1.0 / base as Real;
492+
for c in chars {
493+
n += (c.to_digit(base).ok_or(NumberParseError::InvalidDigit)? as Real) * i;
494+
i /= base as Real;
495+
}
496+
Ok(Atom::Real(if neg { -n } else { n }))
497+
} else {
498+
let mut n = 0;
499+
for c in chars {
500+
n *= base as Integer;
501+
// Negative numbers have a larger range than positive numbers (e.g. i8 has range -128..127)
502+
n -= c.to_digit(base).ok_or(NumberParseError::InvalidDigit)? as Integer;
503+
}
504+
Ok(Atom::Integer(if neg { n } else { -n }))
505+
}
387506
}
388507
}
508+
509+
#[cfg(test)]
510+
mod test {
511+
use super::*;
512+
513+
#[test]
514+
fn number() {
515+
assert_eq!(parse_integer("0"), Ok(Atom::Integer(0)));
516+
assert_eq!(parse_integer("32"), Ok(Atom::Integer(32)));
517+
assert_eq!(parse_integer("0.0"), Ok(Atom::Real(0.0)));
518+
match parse_integer("13.37") {
519+
Ok(Atom::Real(f)) => assert!((f - 13.37).abs() <= Real::EPSILON * 13.37),
520+
r => panic!("{:?}", r),
521+
}
522+
assert_eq!(parse_integer("."), Err(NumberParseError::SeparatorInWrongPosition));
523+
assert_eq!(parse_integer("0."), Err(NumberParseError::SeparatorInWrongPosition));
524+
assert_eq!(parse_integer(".0"), Err(NumberParseError::SeparatorInWrongPosition));
525+
}
526+
}

script/src/bin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ pub fn main() -> Result<(), io::Error> {
1111
Ok(source) => match ballscript::parse(&source) {
1212
Ok(script) => {
1313
let mut script = script.instance();
14+
dbg!(&script);
1415
match script.call("main", &[]) {
1516
Ok(_) => Ok(()),
16-
Err(e) => Err(e).unwrap(),
17+
Err(e) => todo!("{:?}", e),
1718
}
1819
}
1920
Err(e) => Err(e).unwrap(),

0 commit comments

Comments
 (0)