aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdvard Thörnros <edvard.thornros@gmail.com>2021-02-01 20:57:55 +0100
committerEdvard Thörnros <edvard.thornros@gmail.com>2021-02-01 21:38:34 +0100
commite86852857bc6f6ca1e1cd81db494071c5d9e3ff1 (patch)
tree71f111699dd0d7526dbe5ee334c477db6e0362e0
parente9ce94d72e1e8e51b7843b414504a07e132813e1 (diff)
parentfd4868df1fd24c05bce5c92cf8289fa755f25875 (diff)
downloadsylt-e86852857bc6f6ca1e1cd81db494071c5d9e3ff1.tar.gz
Merge branch 'main' into vectors
-rw-r--r--.github/workflows/rust.yml21
-rw-r--r--pong/Cargo.toml16
-rw-r--r--pong/README37
-rw-r--r--pong/pong.tdy199
-rw-r--r--pong/src/main.rs90
-rw-r--r--src/compiler.rs85
-rw-r--r--src/lib.rs42
-rw-r--r--src/tokenizer.rs3
-rw-r--r--src/vm.rs138
9 files changed, 565 insertions, 66 deletions
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..d09f364
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,21 @@
+name: Rust
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build
+ run: cargo build --verbose
+ - name: Run tests
+ run: cargo test --verbose
diff --git a/pong/Cargo.toml b/pong/Cargo.toml
new file mode 100644
index 0000000..acf7d67
--- /dev/null
+++ b/pong/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "pong"
+version = "0.1.0"
+authors = ["Gustav Sörnäs <gustav@sornas.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[profile.release]
+lto = "thin"
+
+[dependencies]
+tihdy = { path = ".."}
+tihdy_derive = { path = "../tihdy_derive" }
+
+macroquad = "0.2"
diff --git a/pong/README b/pong/README
new file mode 100644
index 0000000..be718ec
--- /dev/null
+++ b/pong/README
@@ -0,0 +1,37 @@
+PONG
+====
+
+Made for Global Game Jam 2021 at LiU Game Jam's site.
+
+The language wasn't really ready for a normal game so we wanted to make Pong
+(the easiest game we could think of) while fixing what we needed to make it
+work.
+
+Building
+--------
+
+Clone the entire tihdy-repo, cd into the pong-directory and then cargo run.
+
+Modifying
+---------
+
+All game logic is located in pong.tdy. Most of the configuration should be
+straight forward. If you want to modify the controls, you need to change both
+the string passed from pong.tdy as well as the respective function in src/main.rs
+(key_down).
+
+If you want to add more rust functions (for example, to draw text to the screen)
+you need to add it like the already existing functions. All functions need a
+return type and a return value, even if it is a Type::Void and Value::Nil. (See
+e.g. clear for an example.) For a draw_text you probably want to take a
+Value::String(s), Value::Float(x), Value::Float(y) and so on. Inside the block
+you then write your normal Rusty code.
+
+After writing your function you also need to link it so you can use it from the
+game file. Add it to 'functions' like the other linked functions inside main()
+in src/main.rs.
+
+After linking, the only thing left to do is call it from the game logic. You'll
+get a type error if you pass the wrong parameters. Additional parameter
+combinations can be set (much like normal function overloading) should you have
+the need by adding more "arms" to the extern_function!().
diff --git a/pong/pong.tdy b/pong/pong.tdy
new file mode 100644
index 0000000..15998f3
--- /dev/null
+++ b/pong/pong.tdy
@@ -0,0 +1,199 @@
+PADDLE_W := 0.5
+PADDLE_H := 3.0
+
+blob Paddle {
+ x: float
+ y: float
+}
+
+BALL_S := 0.4
+BALL_STARTV := 5.0
+
+blob Ball {
+ x: float
+ y: float
+
+ vx: float
+ vy: float
+}
+
+blob Player {
+ paddle: Paddle
+ score: float
+}
+
+blob State {
+ p1: Player
+ p2: Player
+ ball: Ball
+}
+
+ball_speed := 2.0
+SPEED := 10.0
+
+abs := fn a: float -> float {
+ if a < 0.0 {
+ ret -a
+ }
+ ret a
+}
+
+and := fn a: bool, b: bool -> bool {
+ if a {
+ ret b
+ }
+ ret false
+}
+
+or := fn a: bool, b: bool -> bool {
+ if a {
+ ret true
+ }
+ ret b
+}
+
+sign := fn a: float -> float {
+ if a < 0.0 {
+ ret -1.0
+ }
+ ret 1.0
+}
+
+rect_overlap := fn ax: float, ay: float, aw: float, ah: float, bx: float, by: float, bw: float, bh: float -> bool {
+ acx := ax + aw / 2.
+ acy := ay + ah / 2.
+
+ bcx := bx + bw / 2.
+ bcy := by + bh / 2.
+
+ dx := abs(acx - bcx) + (-aw - bw) / 2.
+ dy := abs(acy - bcy) + (-ah - bh) / 2.
+ ret and(dx < 0., dy < 0.)
+}
+
+ball_and_paddle_check := fn pad: Paddle, ball: Ball {
+
+ if rect_overlap(ball.x, ball.y, BALL_S, BALL_S, pad.x, pad.y, PADDLE_W, PADDLE_H) {
+ SPEEDUP := 0.5
+ ballcy := ball.y + BALL_S / 2.
+ padcy := pad.y + PADDLE_H / 2.
+ if ball.x < pad.x {
+ ball.vx = -abs(ball.vx) - SPEEDUP
+ ball.vy = ball.vy + (ballcy - padcy) * 3.
+ } else {
+ ball.vx = abs(ball.vx) + SPEEDUP
+ ball.vy = ball.vy + (ballcy - padcy) * 3.
+ }
+ }
+}
+
+update := fn state: State {
+ delta := get_delta()
+ speed := delta * SPEED
+
+ if key_down("w") {
+ state.p1.paddle.y = state.p1.paddle.y - speed
+ }
+ if key_down("s") {
+ state.p1.paddle.y = state.p1.paddle.y + speed
+ }
+
+ if state.p1.paddle.y < 0.0 {
+ state.p1.paddle.y = 0.0
+ }
+
+ if state.p1.paddle.y > 20.0 - PADDLE_H {
+ state.p1.paddle.y = 20.0 - PADDLE_H
+ }
+
+ if key_down("i") {
+ state.p2.paddle.y = state.p2.paddle.y - speed
+ }
+ if key_down("k") {
+ state.p2.paddle.y = state.p2.paddle.y + speed
+ }
+
+ if state.p2.paddle.y < 0.0 {
+ state.p2.paddle.y = 0.0
+ }
+
+ if state.p2.paddle.y > 20.0 - PADDLE_H {
+ state.p2.paddle.y = 20.0 - PADDLE_H
+ }
+
+ state.ball.x = state.ball.x + delta * state.ball.vx
+ state.ball.y = state.ball.y + delta * state.ball.vy
+
+ if state.ball.y < 0. {
+ state.ball.vy = abs(state.ball.vy)
+ }
+
+ if state.ball.y > 20. - 0.2 {
+ state.ball.vy = -abs(state.ball.vy)
+ }
+
+ if state.ball.x < 0.0 {
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = -BALL_STARTV
+ state.ball.vy = 0.
+ state.p1.score = state.p1.score + 1.0
+ }
+
+ if state.ball.x > 20.0 {
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = BALL_STARTV
+ state.ball.vy = 0.
+ state.p2.score = state.p2.score + 1.0
+ }
+
+ ball_and_paddle_check(state.p1.paddle, state.ball)
+ ball_and_paddle_check(state.p2.paddle, state.ball)
+}
+
+draw := fn state: State {
+ clear()
+
+ draw_rectangle(state.p1.paddle.x, state.p1.paddle.y, PADDLE_W, PADDLE_H)
+ draw_rectangle(state.p2.paddle.x, state.p2.paddle.y, PADDLE_W, PADDLE_H)
+
+ for y := 0.0, y < state.p1.score, y = y + 1.0 {
+ draw_rectangle(15.0, y * 0.5, 0.5, 0.5)
+ }
+
+ for y := 0.0, y < state.p2.score, y = y + 1.0 {
+ draw_rectangle(5.0, y * 0.5, 0.5, 0.5)
+ }
+
+ draw_rectangle(state.ball.x, state.ball.y, BALL_S, BALL_S)
+}
+
+init := fn {
+ state := State()
+ state.ball = Ball()
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = BALL_STARTV
+ state.ball.vy = 0.0
+
+ state.p1 = Player()
+ state.p1.score = 0.0
+ state.p1.paddle = Paddle()
+ state.p1.paddle.x = 1.
+ state.p1.paddle.y = 10.
+
+ state.p2 = Player()
+ state.p2.score = 0.0
+ state.p2.paddle = Paddle()
+ state.p2.paddle.x = 19.
+ state.p2.paddle.y = 10.
+
+ for i := 0, i == i, i = i + 1 {
+ update(state)
+ draw(state)
+ yield
+ }
+}
+
+init()
diff --git a/pong/src/main.rs b/pong/src/main.rs
new file mode 100644
index 0000000..2ab4b4a
--- /dev/null
+++ b/pong/src/main.rs
@@ -0,0 +1,90 @@
+use macroquad::{Camera2D, DARKPURPLE, KeyCode, SKYBLUE, clear_background, draw_rectangle, get_frame_time, is_key_down, next_frame, set_camera, vec2};
+use std::path::Path;
+use tihdy::{Type, Value};
+use tihdy::vm::OpResult;
+use tihdy_derive::extern_function;
+
+const SCREEN_WIDTH: f32 = 20.0;
+const SCREEN_HEIGHT: f32 = 20.0;
+
+extern_function!(log
+ [Value::Float(f1), Value::Float(f2)] -> Type::Void => {
+ println!("({}, {})", f1, f2);
+ Ok(Value::Nil)
+ },
+);
+
+extern_function!(get_delta
+ [] -> Type::Float => {
+ Ok(Value::Float(get_frame_time() as f64))
+ },
+);
+
+extern_function!(key_down
+ [Value::String(s)] -> Type::Bool => {
+ let s: &str = s;
+ Ok(Value::Bool(match s {
+ "w" => is_key_down(KeyCode::W),
+ "s" => is_key_down(KeyCode::S),
+ "i" => is_key_down(KeyCode::I),
+ "k" => is_key_down(KeyCode::K),
+ _ => false,
+ }))
+ },
+);
+
+extern_function!(my_draw_rectangle
+ [Value::Float(x), Value::Float(y), Value::Float(w), Value::Float(h)] -> Type::Void => {
+ draw_rectangle(*x as f32, *y as f32, *w as f32, *h as f32, DARKPURPLE);
+ Ok(Value::Nil)
+ },
+);
+
+extern_function!(clear
+ [] -> Type::Void => {
+ clear_background(SKYBLUE);
+ Ok(Value::Nil)
+ },
+);
+
+#[macroquad::main("Pong")]
+async fn main() {
+
+ let functions: Vec<(String, tihdy::RustFunction)> = vec![
+ ("log".to_string(), log),
+ ("get_delta".to_string(), get_delta),
+ ("key_down".to_string(), key_down),
+ ("draw_rectangle".to_string(), my_draw_rectangle),
+ ("clear".to_string(), clear),
+ ];
+
+ let vm = tihdy::compile_file(Path::new("pong.tdy"), false, functions);
+ if let Err(errs) = vm {
+ for err in errs {
+ println!("{}", err);
+ }
+ return;
+ }
+
+ set_camera(Camera2D {
+ zoom: vec2(1. / SCREEN_WIDTH * 2., -1. / SCREEN_HEIGHT * 2.),
+ target: vec2(SCREEN_WIDTH / 2., SCREEN_HEIGHT / 2.),
+ ..Default::default()
+ });
+
+ let mut vm = vm.unwrap();
+ loop {
+ match vm.run() {
+ Err(e) => {
+ println!("{:?}", e);
+ break;
+ }
+ Ok(OpResult::Yield) => {
+ next_frame().await
+ }
+ _ => {
+ break;
+ }
+ }
+ }
+}
diff --git a/src/compiler.rs b/src/compiler.rs
index 6c01ea3..9a1d1e5 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -674,9 +674,38 @@ impl Compiler {
}
}
- fn assign(&mut self, name: &str, block: &mut Block) {
+ fn assign(&mut self, block: &mut Block) {
+ let name = match self.eat() {
+ Token::Identifier(name) => name,
+ _ => {
+ error!(self, format!("Expected identifier in assignment"));
+ return;
+ }
+ };
+
+ let op = match self.eat() {
+ Token::Equal => None,
+
+ Token::PlusEqual => Some(Op::Add),
+ Token::MinusEqual => Some(Op::Sub),
+ Token::StarEqual => Some(Op::Mul),
+ Token::SlashEqual => Some(Op::Div),
+
+ _ => {
+ error!(self, format!("Expected '=' in assignment"));
+ return;
+ }
+ };
+
if let Some(var) = self.find_variable(&name) {
- self.expression(block);
+ if let Some(op) = op {
+ block.add(Op::Copy, self.line());
+ self.expression(block);
+ block.add(op, self.line());
+ } else {
+ self.expression(block);
+ }
+
if var.upvalue {
block.add(Op::AssignUpvalue(var.slot), self.line());
} else {
@@ -901,14 +930,31 @@ impl Compiler {
return Err(());
};
- if self.peek() == Token::Equal {
- self.eat();
- self.expression(block);
- block.add(Op::Set(field), self.line());
- return Ok(());
- } else {
- block.add(Op::Get(field), self.line());
- }
+ let op = match self.peek() {
+ Token::Equal => {
+ self.eat();
+ self.expression(block);
+ block.add(Op::Set(field), self.line());
+ return Ok(());
+ }
+
+ Token::PlusEqual => Op::Add,
+ Token::MinusEqual => Op::Sub,
+ Token::StarEqual => Op::Mul,
+ Token::SlashEqual => Op::Div,
+
+ _ => {
+ block.add(Op::Get(field), self.line());
+ continue;
+ }
+ };
+ block.add(Op::Copy, self.line());
+ block.add(Op::Get(field.clone()), self.line());
+ self.eat();
+ self.expression(block);
+ block.add(op, self.line());
+ block.add(Op::Set(field), self.line());
+ return Ok(());
}
Token::LeftParen => {
self.call(block);
@@ -936,6 +982,16 @@ impl Compiler {
block.add(Op::Print, self.line());
}
+ (Token::Identifier(_), Token::Equal, ..) |
+ (Token::Identifier(_), Token::PlusEqual, ..) |
+ (Token::Identifier(_), Token::MinusEqual, ..) |
+ (Token::Identifier(_), Token::SlashEqual, ..) |
+ (Token::Identifier(_), Token::StarEqual, ..)
+
+ => {
+ self.assign(block);
+ }
+
(Token::Identifier(_), Token::Dot, ..) => {
let block_length = block.ops.len();
let token_length = self.curr;
@@ -958,16 +1014,15 @@ impl Compiler {
}
}
- (Token::Identifier(name), Token::ColonEqual, ..) => {
+ (Token::Yield, ..) => {
self.eat();
- self.eat();
- self.definition_statement(&name, Type::UnknownType, block);
+ block.add(Op::Yield, self.line());
}
- (Token::Identifier(name), Token::Equal, ..) => {
+ (Token::Identifier(name), Token::ColonEqual, ..) => {
self.eat();
self.eat();
- self.assign(&name, block);
+ self.definition_statement(&name, Type::UnknownType, block);
}
(Token::Blob, Token::Identifier(_), ..) => {
diff --git a/src/lib.rs b/src/lib.rs
index d07f2a3..336a7e2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,6 +21,22 @@ pub fn run_file(path: &Path, print: bool, functions: Vec<(String, RustFunction)>
run(tokenizer::file_to_tokens(path), path, print, functions)
}
+pub fn compile_file(path: &Path,
+ print: bool,
+ functions: Vec<(String, RustFunction)>
+ ) -> Result<vm::VM, Vec<Error>> {
+ let tokens = tokenizer::file_to_tokens(path);
+ match compiler::compile("main", path, tokens, &functions) {
+ Ok(prog) => {
+ let mut vm = vm::VM::new().print_blocks(print).print_ops(print);
+ vm.typecheck(&prog)?;
+ vm.init(&prog);
+ Ok(vm)
+ }
+ Err(errors) => Err(errors),
+ }
+}
+
pub fn run_string(s: &str, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
run(tokenizer::string_to_tokens(s), Path::new("builtin"), print, functions)
}
@@ -30,7 +46,8 @@ pub fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String
Ok(prog) => {
let mut vm = vm::VM::new().print_blocks(print).print_ops(print);
vm.typecheck(&prog)?;
- if let Err(e) = vm.run(&prog) {
+ vm.init(&prog);
+ if let Err(e) = vm.run() {
Err(vec![e])
} else {
Ok(())
@@ -316,6 +333,26 @@ a() <=> 4
test_file!(scoping, "tests/scoping.tdy");
test_file!(for_, "tests/for.tdy");
+
+ test_multiple!(
+ op_assign,
+ add: "a := 1\na += 1\na <=> 2",
+ sub: "a := 2\na -= 1\na <=> 1",
+ mul: "a := 2\na *= 2\na <=> 4",
+ div: "a := 2\na /= 2\na <=> 1",
+ cluster: "
+blob A { a: int }
+a := A()
+a.a = 0
+a.a += 1
+a.a <=> 1
+a.a *= 2
+a.a <=> 2
+a.a /= 2
+a.a <=> 1
+a.a -= 1
+a.a <=> 0"
+ );
}
#[derive(Clone)]
@@ -523,6 +560,7 @@ pub enum Op {
Pop,
PopUpvalue,
+ Copy,
Constant(Value),
Tuple(usize),
@@ -561,7 +599,9 @@ pub enum Op {
Call(usize),
Print,
+
Return,
+ Yield,
}
#[derive(Debug)]
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index 7bd0849..28172a3 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -35,6 +35,9 @@ pub enum Token {
#[token("print")]
Print,
+ #[token("yield")]
+ Yield,
+
#[token("ret")]
Ret,
diff --git a/src/vm.rs b/src/vm.rs
index 485d449..246728d 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -66,7 +66,9 @@ pub struct VM {
}
-enum OpResult {
+#[derive(Eq, PartialEq)]
+pub enum OpResult {
+ Yield,
Continue,
Done,
}
@@ -110,8 +112,15 @@ impl VM {
Rc::new(RefCell::new(UpValue::new(slot))))
}
+ fn push(&mut self, value: Value) {
+ self.stack.push(value);
+ }
+
fn pop(&mut self) -> Value {
- self.stack.pop().unwrap()
+ match self.stack.pop() {
+ Some(x) => x,
+ None => self.crash_and_burn(),
+ }
}
fn poppop(&mut self) -> (Value, Value) {
@@ -143,6 +152,17 @@ impl VM {
self.frame().block.borrow().ops[ip].clone()
}
+ fn crash_and_burn(&self) -> ! {
+ println!("\n\n !!!POPPING EMPTY STACK - DUMPING EVERYTHING!!!\n");
+ self.print_stack();
+ println!("\n");
+ self.frame().block.borrow().debug_print();
+ println!(" ip: {}, line: {}\n",
+ self.frame().ip.blue(),
+ self.frame().block.borrow().line(self.frame().ip).blue());
+ unreachable!();
+ }
+
fn error(&self, kind: ErrorKind, message: Option<String>) -> Error {
let frame = self.frames.last().unwrap();
Error {
@@ -164,7 +184,7 @@ impl VM {
}
Op::Pop => {
- self.stack.pop().unwrap();
+ self.pop();
}
Op::Tuple(size) => {
@@ -173,11 +193,22 @@ impl VM {
}
Op::PopUpvalue => {
- let value = self.stack.pop().unwrap();
+ let value = self.pop();
let slot = self.stack.len();
self.drop_upvalue(slot, value);
}
+ Op::Copy => {
+ let v = self.pop();
+ self.push(v.clone());
+ self.push(v);
+ }
+
+ Op::Yield => {
+ self.frame_mut().ip += 1;
+ return Ok(OpResult::Yield);
+ }
+
Op::Constant(value) => {
let offset = self.frame().stack_offset;
let value = match value {
@@ -200,7 +231,7 @@ impl VM {
},
_ => value.clone(),
};
- self.stack.push(value);
+ self.push(value);
}
Op::Index => {
@@ -224,23 +255,23 @@ impl VM {
}
Op::Get(field) => {
- let inst = self.stack.pop();
- if let Some(Value::BlobInstance(ty, values)) = inst {
+ let inst = self.pop();
+ if let Value::BlobInstance(ty, values) = inst {
let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
- self.stack.push(values.borrow()[slot].clone());
+ self.push(values.borrow()[slot].clone());
} else {
- error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst.unwrap()]));
+ error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst]));
}
}
Op::Set(field) => {
- let value = self.stack.pop().unwrap();
- let inst = self.stack.pop();
- if let Some(Value::BlobInstance(ty, values)) = inst {
+ let value = self.pop();
+ let inst = self.pop();
+ if let Value::BlobInstance(ty, values) = inst {
let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
values.borrow_mut()[slot] = value;
} else {
- error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst.unwrap()]));
+ error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst]));
}
}
@@ -272,17 +303,17 @@ impl VM {
}
Op::JmpFalse(line) => {
- if matches!(self.stack.pop(), Some(Value::Bool(false))) {
+ if matches!(self.pop(), Value::Bool(false)) {
self.frame_mut().ip = line;
return Ok(OpResult::Continue);
}
}
Op::Assert => {
- if matches!(self.stack.pop(), Some(Value::Bool(false))) {
+ if matches!(self.pop(), Value::Bool(false)) {
error!(self, ErrorKind::Assert);
}
- self.stack.push(Value::Bool(true));
+ self.push(Value::Bool(true));
}
Op::ReadUpvalue(slot) => {
@@ -293,12 +324,12 @@ impl VM {
}
_ => unreachable!(),
};
- self.stack.push(value);
+ self.push(value);
}
Op::AssignUpvalue(slot) => {
let offset = self.frame().stack_offset;
- let value = self.stack.pop().unwrap();
+ let value = self.pop();
let slot = match &self.stack[offset] {
Value::Function(ups, _) => Rc::clone(&ups[slot]),
_ => unreachable!(),
@@ -308,12 +339,12 @@ impl VM {
Op::ReadLocal(slot) => {
let slot = self.frame().stack_offset + slot;
- self.stack.push(self.stack[slot].clone());
+ self.push(self.stack[slot].clone());
}
Op::AssignLocal(slot) => {
let slot = self.frame().stack_offset + slot;
- self.stack[slot] = self.stack.pop().unwrap();
+ self.stack[slot] = self.pop();
}
Op::Define(_) => {}
@@ -329,8 +360,8 @@ impl VM {
values.push(Value::Nil);
}
- self.stack.pop();
- self.stack.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
+ self.pop();
+ self.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
}
Value::Function(_, block) => {
let inner = block.borrow();
@@ -359,7 +390,7 @@ impl VM {
Err(ek) => error!(self, ek, "Wrong arguments to external function".to_string()),
};
self.stack.truncate(new_base);
- self.stack.push(res);
+ self.push(res);
}
_ => {
unreachable!()
@@ -368,7 +399,7 @@ impl VM {
}
Op::Print => {
- println!("PRINT: {:?}", self.stack.pop().unwrap());
+ println!("PRINT: {:?}", self.pop());
}
Op::Return => {
@@ -376,7 +407,7 @@ impl VM {
if self.frames.is_empty() {
return Ok(OpResult::Done);
} else {
- self.stack[last.stack_offset] = self.stack.pop().unwrap();
+ self.stack[last.stack_offset] = self.pop();
for slot in last.stack_offset+1..self.stack.len() {
if self.upvalues.contains_key(&slot) {
let value = self.stack[slot].clone();
@@ -408,20 +439,23 @@ impl VM {
self.frame().block.borrow().ops[self.frame().ip]);
}
- pub fn run(&mut self, prog: &Prog) -> Result<(), Error>{
+ pub fn init(&mut self, prog: &Prog) {
let block = Rc::clone(&prog.blocks[0]);
self.blobs = prog.blobs.clone();
self.extern_functions = prog.functions.clone();
self.stack.clear();
self.frames.clear();
- self.stack.push(Value::Function(Vec::new(), Rc::clone(&block)));
+ self.push(Value::Function(Vec::new(), Rc::clone(&block)));
self.frames.push(Frame {
stack_offset: 0,
block,
ip: 0
});
+ }
+
+ pub fn run(&mut self) -> Result<OpResult, Error> {
if self.print_blocks {
println!("\n [[{}]]\n", "RUNNING".red());
@@ -433,8 +467,9 @@ impl VM {
self.print_stack()
}
- if matches!(self.eval_op(self.op())?, OpResult::Done) {
- return Ok(());
+ let op = self.eval_op(self.op())?;
+ if matches!(op, OpResult::Done | OpResult::Yield) {
+ return Ok(op);
}
}
}
@@ -445,10 +480,12 @@ impl VM {
Op::Jmp(_line) => {}
+ Op::Yield => {}
+
Op::Constant(ref value) => {
match value.clone() {
Value::Function(_, block) => {
- self.stack.push(Value::Function(Vec::new(), block.clone()));
+ self.push(Value::Function(Vec::new(), block.clone()));
let mut types = Vec::new();
for (slot, is_up, ty) in block.borrow().ups.iter() {
@@ -477,25 +514,26 @@ impl VM {
};
},
_ => {
- self.stack.push(value.clone());
+ self.push(value.clone());
}
}
}
Op::Get(field) => {
- let inst = self.stack.pop();
- if let Some(Value::BlobInstance(ty, _)) = inst {
+ let inst = self.pop();
+ if let Value::BlobInstance(ty, _) = inst {
let value = self.blobs[ty].name_to_field.get(&field).unwrap().1.as_value();
- self.stack.push(value);
+ self.push(value);
} else {
- self.stack.push(Value::Nil);
- error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst.unwrap()]));
+ self.push(Value::Nil);
+ error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst]));
}
}
Op::Set(field) => {
- let value = self.stack.pop().unwrap();
- let inst = self.stack.pop().unwrap();
+ let value = self.pop();
+ let inst = self.pop();
+
if let Value::BlobInstance(ty, _) = inst {
let ty = &self.blobs[ty].name_to_field.get(&field).unwrap().1;
if ty != &Type::from(&value) {
@@ -507,17 +545,17 @@ impl VM {
}
Op::PopUpvalue => {
- self.stack.pop().unwrap();
+ self.pop();
}
Op::ReadUpvalue(slot) => {
let value = self.frame().block.borrow().ups[slot].2.as_value();
- self.stack.push(value);
+ self.push(value);
}
Op::AssignUpvalue(slot) => {
let var = self.frame().block.borrow().ups[slot].2.clone();
- let up = self.stack.pop().unwrap().as_type();
+ let up = self.pop().as_type();
if var != up {
error!(self, ErrorKind::TypeError(op, vec![var, up]),
"Incorrect type for upvalue.".to_string());
@@ -525,7 +563,7 @@ impl VM {
}
Op::Return => {
- let a = self.stack.pop().unwrap();
+ let a = self.pop();
let inner = self.frame().block.borrow();
let ret = inner.ret();
if a.as_type() != *ret {
@@ -571,8 +609,8 @@ impl VM {
values[*slot] = ty.as_value();
}
- self.stack.pop();
- self.stack.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
+ self.pop();
+ self.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
}
Value::Function(_, block) => {
let inner = block.borrow();
@@ -603,12 +641,12 @@ impl VM {
Ok(value) => value,
Err(ek) => {
self.stack.truncate(new_base);
- self.stack.push(Value::Nil);
+ self.push(Value::Nil);
error!(self, ek, "Wrong arguments to external function".to_string())
}
};
self.stack.truncate(new_base);
- self.stack.push(res);
+ self.push(res);
}
_ => {
error!(self,
@@ -637,9 +675,9 @@ impl VM {
self.stack.clear();
self.frames.clear();
- self.stack.push(Value::Function(Vec::new(), Rc::clone(&block)));
+ self.push(Value::Function(Vec::new(), Rc::clone(&block)));
for arg in block.borrow().args() {
- self.stack.push(arg.as_value());
+ self.push(arg.as_value());
}
self.frames.push(Frame {
@@ -670,8 +708,8 @@ impl VM {
}
if !self.stack.is_empty() {
- let ident = self.stack.pop().unwrap().identity();
- self.stack.push(ident);
+ let ident = self.pop().identity();
+ self.push(ident);
}
}
errors