Compare commits
No commits in common. "019d780f85d6d331ff8b75599de1ba53c36b1c7c" and "36ed61b8bbe08456baab19c091c21afc6de711f5" have entirely different histories.
019d780f85
...
36ed61b8bb
4 changed files with 5 additions and 149 deletions
|
|
@ -430,7 +430,7 @@ impl<'a> CParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_start(&mut self, kind: TokenKind) {
|
fn expect_start(&mut self, kind: TokenKind) {
|
||||||
assert!(self.eat(kind), "should have started with {kind:?}");
|
assert!(self.eat(kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance_with_error<T>(&mut self, error: T) -> MarkClosed
|
fn advance_with_error<T>(&mut self, error: T) -> MarkClosed
|
||||||
|
|
@ -708,7 +708,7 @@ fn block(p: &mut CParser) {
|
||||||
while !p.at(TokenKind::RightBrace) && !p.eof() {
|
while !p.at(TokenKind::RightBrace) && !p.eof() {
|
||||||
statement(p);
|
statement(p);
|
||||||
}
|
}
|
||||||
p.expect(TokenKind::RightBrace, "expect '}' to end a block");
|
p.expect(TokenKind::RightBrace, "expect '}' to start a block");
|
||||||
|
|
||||||
p.end(m, TreeKind::Block);
|
p.end(m, TreeKind::Block);
|
||||||
}
|
}
|
||||||
|
|
@ -756,7 +756,6 @@ fn statement_return(p: &mut CParser) {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
|
|
||||||
p.expect_start(TokenKind::Return);
|
p.expect_start(TokenKind::Return);
|
||||||
// TODO: Make expression optional if we're returning ()
|
|
||||||
expression(p);
|
expression(p);
|
||||||
if !p.at(TokenKind::RightBrace) {
|
if !p.at(TokenKind::RightBrace) {
|
||||||
p.expect(TokenKind::Semicolon, "expect ';' to end a return statement");
|
p.expect(TokenKind::Semicolon, "expect ';' to end a return statement");
|
||||||
|
|
@ -939,19 +938,13 @@ fn conditional(p: &mut CParser) -> MarkClosed {
|
||||||
|
|
||||||
p.expect_start(TokenKind::If);
|
p.expect_start(TokenKind::If);
|
||||||
expression(p);
|
expression(p);
|
||||||
if p.at(TokenKind::LeftBrace) {
|
block(p);
|
||||||
block(p)
|
|
||||||
} else {
|
|
||||||
p.error("expected a block after `if`")
|
|
||||||
}
|
|
||||||
if p.eat(TokenKind::Else) {
|
if p.eat(TokenKind::Else) {
|
||||||
if p.at(TokenKind::If) {
|
if p.at(TokenKind::If) {
|
||||||
// Don't require another block, just jump right into the conditional.
|
// Don't require another block, just jump right into the conditional.
|
||||||
conditional(p);
|
conditional(p);
|
||||||
} else if p.at(TokenKind::LeftBrace) {
|
|
||||||
block(p);
|
|
||||||
} else {
|
} else {
|
||||||
p.error("expected a block after `else`")
|
block(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1741,7 +1741,7 @@ impl<'a> Semantics<'a> {
|
||||||
eprintln!("\n{}", self.syntax_tree.dump(true));
|
eprintln!("\n{}", self.syntax_tree.dump(true));
|
||||||
|
|
||||||
{
|
{
|
||||||
let errors = self.snapshot_errors();
|
let errors = self.errors.borrow();
|
||||||
if errors.len() == 0 {
|
if errors.len() == 0 {
|
||||||
eprintln!("There were no errors reported during checking.\n");
|
eprintln!("There were no errors reported during checking.\n");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
// Examples of alternate types/union types/heterogeneous types.
|
|
||||||
class Foo {
|
|
||||||
x: f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Bar {
|
|
||||||
y: f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
fun extract_value(v: Foo or Bar) -> f64 {
|
|
||||||
if match v as Foo {
|
|
||||||
v.x // Magic type binding!
|
|
||||||
} else as Bar {
|
|
||||||
v.y // Same magic re-binding of the variable to the new type
|
|
||||||
} // No error; exhaustivity analysis should work.
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is Bob Nystrom's example from
|
|
||||||
// https://journal.stuffwithstuff.com/2023/08/04/representing-heterogeneous-data/
|
|
||||||
//
|
|
||||||
class MeleeWeapon {
|
|
||||||
damage: f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RangedWeapon {
|
|
||||||
minRange: f64;
|
|
||||||
maxRange: f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Monster {
|
|
||||||
health: f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
fun print(x:string) {}
|
|
||||||
|
|
||||||
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
|
|
||||||
if match weapon as w : RangedWeapon {
|
|
||||||
distance >= w.minRange and distance <= w.maxRange
|
|
||||||
} else {
|
|
||||||
distance == 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
|
||||||
// This is worse but it works.
|
|
||||||
if match weapon as MeleeWeapon and distance > 1 or
|
|
||||||
match weapon as w:RangedWeapon and (distance < w.minRange or distance > w.maxRange) {
|
|
||||||
print("You are out of range")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bob says he doesn't want to do flow analysis but we're doing all our
|
|
||||||
// tricks with variables and scoping here, with one *big* bit of magic which
|
|
||||||
// is...
|
|
||||||
//
|
|
||||||
// NOTE: special syntax here: `identifier` as `TypeExpression` ALMOST means
|
|
||||||
// `identifier as identifier : TypeExpression` as the shorthand for checking
|
|
||||||
// local variables. The *almost* part is that the effective type of the
|
|
||||||
// variable changes but not the binding. (Is this what we want?)
|
|
||||||
let damage = if match weapon as w: MeleeWeapon {
|
|
||||||
roll_dice(w.damage)
|
|
||||||
} else as w: RangedWeapon {
|
|
||||||
// This is the trick here: else as re-uses the expression from the match
|
|
||||||
// and so we can do exhaustivity analysis.
|
|
||||||
w.maxRange - w.minRange
|
|
||||||
};
|
|
||||||
|
|
||||||
if monster.health <= damage {
|
|
||||||
print("You kill the monster!");
|
|
||||||
monster.health = 0
|
|
||||||
} else {
|
|
||||||
print("You wound the monster.");
|
|
||||||
monster.health = monster.health - damage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () {
|
|
||||||
if match weapon as w: RangedWeapon and w.maxRange > 10 {
|
|
||||||
// w is still in scope here; the `and` is bound into a predicate expression
|
|
||||||
// and breaks exhaustivity
|
|
||||||
w.minRange
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some fun with iterators
|
|
||||||
|
|
||||||
class Iterator {
|
|
||||||
current: f64;
|
|
||||||
|
|
||||||
fun next(self) -> f64 or () {
|
|
||||||
if self.current < 10 {
|
|
||||||
let result = self.current;
|
|
||||||
self.current = self.current + 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun test() -> f64 {
|
|
||||||
let sum = 0;
|
|
||||||
|
|
||||||
// A single step of an iterator...
|
|
||||||
let it = new Iterator { current: 0 };
|
|
||||||
if match it.next() as v: f64 {
|
|
||||||
sum = sum + v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looping by hand over an iterator is pretty clean.
|
|
||||||
let it = new Iterator { current: 0 };
|
|
||||||
while match it.next() as v: f64 {
|
|
||||||
sum = sum + v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unroll by hand...
|
|
||||||
let it = new Iterator { current: 0 };
|
|
||||||
loop {
|
|
||||||
if match it.next() as v: f64 {
|
|
||||||
sum = sum + v;
|
|
||||||
} else {
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not in this test but `for` over an object should turn into something
|
|
||||||
// like the above.
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ignore WIP
|
|
||||||
// @no-errors
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
fun test() {
|
|
||||||
if true true { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: These errors should be better
|
|
||||||
// @expect-errors:
|
|
||||||
// | 2:10: Error at 'true': expected a block after `if`
|
|
||||||
// | 2:15: Error at '{': expect ';' to end an expression statement
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue