From 7a260cd68fe2c783f3ec3f82cccd1b5376e54e9b Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 14 May 2016 14:59:06 +0200 Subject: [PATCH 01/17] Added "load_meta_url" intrinsic - Added hyper as dependency --- Cargo.toml | 1 + source/test.dyon | 10 +++++++- src/intrinsics/meta.rs | 55 ++++++++++++++++++++++++++++++++++++------ src/intrinsics/mod.rs | 31 +++++++++++++++++++++++- src/lib.rs | 1 + 5 files changed, 88 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 702bb4e5..9f8c4c61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ piston_meta = "0.26.1" range = "0.3.1" rand = "0.3.13" read_color = "0.1.0" +hyper = "0.9.4" diff --git a/source/test.dyon b/source/test.dyon index 8954eea8..52577d66 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,4 +1,12 @@ +fn foo() -> res { + data := load( + meta: "assets/test_meta.txt", + url: "https://raw.githubusercontent.com/PistonDevelopers/dyon/master/assets/test_data.txt" + )? + return ok(data) +} + fn main() { - data := unwrap(load(meta: "assets/test_meta.txt", file: "assets/test_data.txt")) + data := unwrap(foo()) println(data) } diff --git a/src/intrinsics/meta.rs b/src/intrinsics/meta.rs index fd1d804d..2ab14a41 100644 --- a/src/intrinsics/meta.rs +++ b/src/intrinsics/meta.rs @@ -14,16 +14,9 @@ fn io_error(action: &str, file: &str, err: &io::Error) -> String { }) } -/// Loads a file using a meta file as syntax. -pub fn load_meta_file(meta: &str, file: &str) -> Result, String> { - let mut syntax_file = try!(File::open(meta).map_err(|err| io_error("open", meta, &err))); - let mut s = String::new(); - try!(syntax_file.read_to_string(&mut s).map_err(|err| io_error("read", meta, &err))); +fn load_metarules_data(meta: &str, s: &str, file: &str, d: &str) -> Result, String> { let rules = try!(syntax_errstr(&s).map_err(|err| format!("When parsing meta syntax in `{}`:\n{}", meta, err))); - let mut data_file = try!(File::open(file).map_err(|err| io_error("open", file, &err))); - let mut d = String::new(); - try!(data_file.read_to_string(&mut d).map_err(|err| io_error("read", file, &err))); let mut tokens = vec![]; try!(parse_errstr(&rules, &d, &mut tokens).map_err(|err| format!("When parsing data in `{}`:\n{}", file, err))); @@ -66,3 +59,49 @@ pub fn load_meta_file(meta: &str, file: &str) -> Result, String> { } Ok(res) } + +/// Loads a file using a meta file as syntax. +pub fn load_meta_file(meta: &str, file: &str) -> Result, String> { + let mut syntax_file = try!(File::open(meta).map_err(|err| io_error("open", meta, &err))); + let mut s = String::new(); + try!(syntax_file.read_to_string(&mut s).map_err(|err| io_error("read", meta, &err))); + let mut data_file = try!(File::open(file).map_err(|err| io_error("open", file, &err))); + let mut d = String::new(); + try!(data_file.read_to_string(&mut d).map_err(|err| io_error("read", file, &err))); + load_metarules_data(meta, &s, file, &d) +} + +/// Loads a text file from url. +fn load_text_file_from_url(url: &str) -> Result { + use hyper::client::Client; + use hyper::{Url}; + use hyper::status::StatusCode; + use std::io::Read; + + let url_address = try!(Url::parse(url) + .map_err(|e| format!("Error parsing url:\n`{}`\n", e))); + let client = Client::new(); + let request = client.get(url_address); + let mut response = try!(request.send() + .map_err(|e| format!("Error fetching file over http `{}`:\n{}\n", + url, e.to_string()))); + if response.status == StatusCode::Ok { + let mut data = String::new(); + try!(response.read_to_string(&mut data) + .map_err(|e| format!("Error fetching file over http `{}`:\n{}\n", + url, e.to_string()))); + Ok(data) + } else { + Err(format!("Error fetching file over http `{}:\n{}\n", + url, response.status)) + } +} + +/// Loads an url using a meta file as syntax. +pub fn load_meta_url(meta: &str, url: &str) -> Result, String> { + let mut syntax_file = try!(File::open(meta).map_err(|err| io_error("open", meta, &err))); + let mut s = String::new(); + try!(syntax_file.read_to_string(&mut s).map_err(|err| io_error("read", meta, &err))); + let d = try!(load_text_file_from_url(url)); + load_metarules_data(meta, &s, url, &d) +} diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index a95ce6a5..70f4505c 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -132,6 +132,11 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { tys: vec![Type::Text; 2], ret: Type::Result(Box::new(Type::array())) }); + f.insert(Arc::new("load_meta_url".into()), PreludeFunction { + lts: vec![Lt::Default; 2], + tys: vec![Type::Text; 2], + ret: Type::Result(Box::new(Type::array())) + }); } enum EscapeString { @@ -1106,7 +1111,31 @@ pub fn call_standard( })); rt.pop_fn(call.name.clone()); Expect::Something - + } + "load_meta_url" => { + rt.push_fn(call.name.clone(), None, st + 1, lc); + let url = rt.stack.pop().expect(TINVOTS); + let meta = rt.stack.pop().expect(TINVOTS); + let url = match rt.resolve(&url) { + &Variable::Text(ref url) => url.clone(), + x => return Err(module.error(call.args[1].source_range(), + &rt.expected(x, "str"))) + }; + let meta = match rt.resolve(&meta) { + &Variable::Text(ref meta) => meta.clone(), + x => return Err(module.error(call.args[0].source_range(), + &rt.expected(x, "str"))) + }; + let res = meta::load_meta_url(&**meta, &**url); + rt.stack.push(Variable::Result(match res { + Ok(res) => Ok(Box::new(Variable::Array(Arc::new(res)))), + Err(err) => Err(Box::new(Error { + message: Variable::Text(Arc::new(err)), + trace: vec![] + })) + })); + rt.pop_fn(call.name.clone()); + Expect::Something } _ => return Err(module.error(call.source_range, &format!("{}\nUnknown function `{}`", rt.stack_trace(), call.name))) diff --git a/src/lib.rs b/src/lib.rs index b1ad1fde..bbc44e01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate piston_meta; extern crate rand; extern crate range; extern crate read_color; +extern crate hyper; use std::any::Any; use std::sync::{Arc, Mutex}; From 1318515e96300ee77efc5529bc4fe3b311420381 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 14 May 2016 21:44:40 +0200 Subject: [PATCH 02/17] Added `go` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added `go` keyword to syntax - Added “typechk/go.dyon” - Added `ast::Expression::Go` - Added “join_thread” intrinsic - Added `Thread` struct - Added `Variable::Thread` - Added unsafe impl of `Send` for `Variable` - Made `Module` clone-able by putting `Module::ext_prelude` in `Arc<_>` - Added `Kind::Go` - Require `->` on `go` functions in through type check - Switched from `ThreadRng` to `StdRng` to make new runtime work in `move || { … }` - Added `Runtime::go` --- assets/syntax.txt | 11 +++--- source/test.dyon | 42 ++++++++++++++++++++-- source/typechk/go.dyon | 11 ++++++ src/ast.rs | 51 +++++++++++++++++++++++++++ src/intrinsics/mod.rs | 37 ++++++++++++++++++-- src/lib.rs | 73 ++++++++++++++++++++++++++++++++++++--- src/lifetime/kind.rs | 2 ++ src/lifetime/node.rs | 1 + src/lifetime/typecheck.rs | 18 ++++++++++ src/prelude.rs | 2 +- src/runtime.rs | 72 ++++++++++++++++++++++++++++++++++++-- tests/lib.rs | 1 + 12 files changed, 302 insertions(+), 19 deletions(-) create mode 100644 source/typechk/go.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index d10c8e80..88880581 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -84,10 +84,11 @@ _seps: "(){}[],.:;=<>*·+-/%^?|&∧∨!¬∑∃∀\n" 25 call = [.._seps!:"name" ?w "(" .s?.(, arg_expr:"call_arg") ?w ")"] 26 named_call = [.._seps!:"word" ?w "(" ?w .s?.(, [.._seps!:"word" ?w ":" ?w arg_expr:"call_arg" ?w]) ")"] -27 assign = [lexpr:"left" ?w assign_op ?w expr:"right"] -28 assign_op = {":=":":=" "=":"=" "+=":"+=" "-=":"-=" "*=":"*=" "/=":"/=" "%=":"%="} -29 compare = [lexpr:"left" ?w compare_op ?w expr:"right"] -30 compare_op = {"==":"==" "!=":"!=" "¬=":"!=" "<=":"<=" "<":"<" ">=":">=" ">":">"} +27 go = ["go" ?w {call:"call" named_call:"named_call"}] +28 assign = [lexpr:"left" ?w assign_op ?w expr:"right"] +29 assign_op = {":=":":=" "=":"=" "+=":"+=" "-=":"-=" "*=":"*=" "/=":"/=" "%=":"%="} +30 compare = [lexpr:"left" ?w compare_op ?w expr:"right"] +31 compare_op = {"==":"==" "!=":"!=" "¬=":"!=" "<=":"<=" "<":"<" ">=":">=" ">":">"} 40 label = ?["'" .._seps!:"label" ?w ":" ?w] 41 short_body = [.w! .._seps!:"name" ?w {["[" ?w expr:"start" , expr:"end" ?w ")"] @@ -96,7 +97,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?|&∧∨!¬∑∃∀\n" 43 , = [?w "," ?w] 44 arr = {array:"array" array_fill:"array_fill"} 45 items = {vec4:"vec4" ["(" ?w expr ?w ")"] unop:"unop" - text call:"call" named_call:"named_call" + text go:"go" call:"call" named_call:"named_call" num bool color item:"item"} 50 short_loops = {sum:"sum" min:"min" max:"max" sift:"sift" any:"any" all:"all"} diff --git a/source/test.dyon b/source/test.dyon index 52577d66..ffbb85a1 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,12 +1,48 @@ -fn foo() -> res { +fn get_url(url) -> res { data := load( meta: "assets/test_meta.txt", - url: "https://raw.githubusercontent.com/PistonDevelopers/dyon/master/assets/test_data.txt" + url: url )? return ok(data) } +fn foo(n, a: [{}]) -> { + println(n) + println(a[0].x) + return sum i n { + sleep(0.1) + i+1 + } +} + +fn bar() {} + +fn bar2() -> f64 { + return 2 +} + fn main() { - data := unwrap(foo()) + url := "https://goo.gl/IzuRJU" + + b := {x: "Hi!"} + a := [b] + + // data_thread := go get_url(url) + data_thread := go foo(4, a) + // data_thread := go bar() + + for i 10 { + sleep(0.1) + } + println("Done here") + data := unwrap(join(thread: data_thread)) println(data) } + +/* +fn main() { + if go bar2() { + + } +} +*/ diff --git a/source/typechk/go.dyon b/source/typechk/go.dyon new file mode 100644 index 00000000..2adba174 --- /dev/null +++ b/source/typechk/go.dyon @@ -0,0 +1,11 @@ +fn bar() {} + +fn main() { + data_thread := go bar() + for i 10 { + sleep(0.1) + } + println("Done here") + data := unwrap(join(thread: data_thread)) + println(data) +} diff --git a/src/ast.rs b/src/ast.rs index 803e2d5d..e7500512 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -233,6 +233,7 @@ pub enum Expression { Break(Break), Continue(Continue), Block(Block), + Go(Box), Call(Call), Item(Item), BinOp(Box), @@ -256,6 +257,9 @@ pub enum Expression { Try(Box), } +// Required because the `Sync` impl of `Variable` is unsafe. +unsafe impl Sync for Expression {} + impl Expression { pub fn from_meta_data( node: &str, @@ -347,6 +351,10 @@ impl Expression { } else { return Err(()); } + } else if let Ok((range, val)) = Go::from_meta_data( + convert, ignored) { + convert.update(range); + result = Some(Expression::Go(Box::new(val))); } else if let Ok((range, val)) = Call::from_meta_data( convert, ignored) { convert.update(range); @@ -433,6 +441,7 @@ impl Expression { Break(ref br) => br.source_range, Continue(ref c) => c.source_range, Block(ref bl) => bl.source_range, + Go(ref go) => go.source_range, Call(ref call) => call.source_range, Item(ref it) => it.source_range, BinOp(ref binop) => binop.source_range, @@ -985,6 +994,48 @@ impl Item { } } +#[derive(Debug, Clone)] +pub struct Go { + pub call: Call, + pub source_range: Range, +} + +impl Go { + pub fn from_meta_data( + mut convert: Convert, + ignored: &mut Vec + ) -> Result<(Range, Go), ()> { + let start = convert.clone(); + let node = "go"; + let start_range = try!(convert.start_node(node)); + convert.update(start_range); + + let mut call: Option = None; + loop { + if let Ok(range) = convert.end_node(node) { + convert.update(range); + break; + } else if let Ok((range, val)) = Call::from_meta_data(convert, ignored) { + convert.update(range); + call = Some(val); + } else if let Ok((range, val)) = Call::named_from_meta_data(convert, ignored) { + convert.update(range); + call = Some(val); + } else { + let range = convert.ignore(); + convert.update(range); + ignored.push(range); + } + } + + let call = try!(call.ok_or(())); + Ok((convert.subtract(start), Go { + call: call, + source_range: convert.source(start).unwrap(), + })) + } +} + #[derive(Debug, Clone)] pub struct Call { pub name: Arc, diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 70f4505c..23eefbd9 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -137,6 +137,7 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { tys: vec![Type::Text; 2], ret: Type::Result(Box::new(Type::array())) }); + sarg(f, "join_thread", Type::Any, Type::Result(Box::new(Type::Any))); } enum EscapeString { @@ -641,6 +642,7 @@ pub fn call_standard( &Variable::RustObject(_) => rt.rust_object_type.clone(), &Variable::Option(_) => rt.option_type.clone(), &Variable::Result(_) => rt.result_type.clone(), + &Variable::Thread(_) => rt.thread_type.clone(), }; rt.stack.push(v); rt.pop_fn(call.name.clone()); @@ -667,7 +669,7 @@ pub fn call_standard( let v = match rt.resolve(&v) { &Variable::Text(ref text) => { let mut m = Module::new(); - for (key, &(ref f, ref ext)) in &module.ext_prelude { + for (key, &(ref f, ref ext)) in &*module.ext_prelude { m.add(key.clone(), *f, ext.clone()); } if let Err(err) = load(text, &mut m) { @@ -697,7 +699,7 @@ pub fn call_standard( let modules = rt.stack.pop().expect(TINVOTS); let source = rt.stack.pop().expect(TINVOTS); let mut new_module = Module::new(); - for (key, &(ref f, ref ext)) in &module.ext_prelude { + for (key, &(ref f, ref ext)) in &*module.ext_prelude { new_module.add(key.clone(), *f, ext.clone()); } match rt.resolve(&modules) { @@ -909,7 +911,7 @@ pub fn call_standard( obj.insert(arguments.clone(), Variable::Array(Arc::new(args))); functions.push(Variable::Object(Arc::new(obj))); } - for (f_name, &(_, ref f)) in &module.ext_prelude { + for (f_name, &(_, ref f)) in &*module.ext_prelude { let mut obj = HashMap::new(); obj.insert(name.clone(), Variable::Text(f_name.clone())); obj.insert(returns.clone(), Variable::Bool(f.returns())); @@ -1137,6 +1139,35 @@ pub fn call_standard( rt.pop_fn(call.name.clone()); Expect::Something } + "join_thread" => { + use Thread; + + rt.push_fn(call.name.clone(), None, st + 1, lc); + let thread = rt.stack.pop().expect(TINVOTS); + let handle_res = Thread::invalidate_handle(rt, thread); + rt.stack.push(Variable::Result({ + match handle_res { + Ok(handle) => { + match handle.join() { + Ok(res) => Ok(Box::new(res)), + Err(_err) => Err(Box::new(Error { + message: Variable::Text(Arc::new( + "Thread did not exit successfully".into())), + trace: vec![] + })) + } + } + Err(err) => { + Err(Box::new(Error { + message: Variable::Text(Arc::new(err)), + trace: vec![] + })) + } + } + })); + rt.pop_fn(call.name.clone()); + Expect::Something + } _ => return Err(module.error(call.source_range, &format!("{}\nUnknown function `{}`", rt.stack_trace(), call.name))) }; diff --git a/src/lib.rs b/src/lib.rs index bbc44e01..4d321c72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ extern crate read_color; extern crate hyper; use std::any::Any; +use std::fmt; +use std::thread::JoinHandle; use std::sync::{Arc, Mutex}; use std::collections::HashMap; use range::Range; @@ -34,6 +36,56 @@ pub struct Error { pub trace: Vec, } +#[derive(Clone)] +pub struct Thread { + pub handle: Option>>>, +} + +impl Thread { + pub fn new(handle: JoinHandle) -> Thread { + Thread { + handle: Some(Arc::new(Mutex::new(handle))) + } + } + + /// Removes the thread handle from the stack. + /// This is to prevent an extra reference when resolving the variable. + pub fn invalidate_handle( + rt: &mut Runtime, + var: Variable + ) -> Result, String> { + use std::error::Error; + + let thread = match var { + Variable::Ref(ind) => { + use std::mem::replace; + + match replace(&mut rt.stack[ind], Variable::Thread(Thread { handle: None })) { + Variable::Thread(th) => th, + x => return Err(rt.expected(&x, "Thread")) + } + } + Variable::Thread(thread) => thread, + x => return Err(rt.expected(&x, "Thread")) + }; + let handle = match thread.handle { + None => return Err("The Thread has already been invalidated".into()), + Some(thread) => thread + }; + let mutex = try!(Arc::try_unwrap(handle).map_err(|_| + format!("{}\nCan not access Thread because there is \ + more than one reference to it", rt.stack_trace()))); + mutex.into_inner().map_err(|err| + format!("{}\nCan not lock Thread mutex:\n{}", rt.stack_trace(), err.description())) + } +} + +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "thread") + } +} + #[derive(Debug, Clone)] pub enum Variable { Ref(usize), @@ -48,8 +100,16 @@ pub enum Variable { RustObject(RustObject), Option(Option>), Result(Result, Box>), + Thread(Thread), } +/* +This is requires because `UnsafeRef(*mut Variable)` can not be sent across threads. +The lack of `UnsafeRef` variant when sending across threads is guaranteed at language level. +TODO: Make the interior of `UnsafeRef` inaccessible outside the library. +*/ +unsafe impl Send for Variable {} + impl Variable { fn deep_clone(&self, stack: &Vec) -> Variable { use Variable::*; @@ -86,6 +146,7 @@ impl Variable { Result(Ok(ref ok)) => Result(Ok(ok.clone())), // `err(x)` always uses deep clone, so it does not contain references. Result(Err(ref err)) => Result(Err(err.clone())), + Thread(_) => self.clone(), } } } @@ -107,11 +168,12 @@ impl PartialEq for Variable { } } +#[derive(Clone)] pub struct Module { pub source: Option, pub functions: HashMap, Arc>, - pub ext_prelude: HashMap, - (fn(&mut Runtime) -> Result<(), String>, PreludeFunction)>, + pub ext_prelude: Arc, + (fn(&mut Runtime) -> Result<(), String>, PreludeFunction)>>, } impl Module { @@ -119,7 +181,7 @@ impl Module { Module { source: None, functions: HashMap::new(), - ext_prelude: HashMap::new(), + ext_prelude: Arc::new(HashMap::new()), } } @@ -144,7 +206,10 @@ impl Module { f: fn(&mut Runtime) -> Result<(), String>, prelude_function: PreludeFunction ) { - self.ext_prelude.insert(name.clone(), (f, prelude_function)); + Arc::get_mut(&mut self.ext_prelude) + .expect("Can not add prelude function when there is \ + more than one reference to the module") + .insert(name.clone(), (f, prelude_function)); } } diff --git a/src/lifetime/kind.rs b/src/lifetime/kind.rs index 33e6eec4..cbf070e3 100644 --- a/src/lifetime/kind.rs +++ b/src/lifetime/kind.rs @@ -61,6 +61,7 @@ pub enum Kind { Res, RetType, ReturnVoid, + Go, } impl Kind { @@ -127,6 +128,7 @@ impl Kind { "res" => Kind::Res, "ret_type" => Kind::RetType, "return_void" => Kind::ReturnVoid, + "go" => Kind::Go, _ => return None }) } diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 75917628..80e96482 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -133,6 +133,7 @@ impl Node { let mut call_arg_ind = 0; for &c in &self.children { match (self.kind, nodes[c].kind) { + (_, Kind::Go) => {} (_, Kind::ForN) => {} (_, Kind::Continue) => {} (_, Kind::Sift) => {} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index a6b340fa..546d1da8 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -13,6 +13,9 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range let kind = nodes[i].kind; let mut this_ty = None; match kind { + Kind::Go => { + // TODO: Infer thread type from function. + } Kind::Call => { if let Some(decl) = nodes[i].declaration { let mut missing = false; @@ -211,6 +214,21 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range )); } } + Kind::Go => { + if nodes[i].children.len() > 0 { + if let Some(decl) = nodes[nodes[i].children[0]].declaration { + match nodes[decl].ty { + None | Some(Type::Void) => { + return Err(nodes[i].source.wrap( + format!("Requires `->` on `{}`", + nodes[decl].name.as_ref().unwrap()) + )); + } + _ => {} + } + } + } + } Kind::If => { try!(check_if(i, nodes)) } diff --git a/src/prelude.rs b/src/prelude.rs index 64ccf63c..0f874228 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -63,7 +63,7 @@ impl Prelude { pub fn from_module(module: &Module) -> Prelude { let mut functions = HashMap::new(); intrinsics::standard(&mut functions); - for (key, &(_, ref val)) in &module.ext_prelude { + for (key, &(_, ref val)) in &*module.ext_prelude { functions.insert(key.clone(), val.clone()); } for f in module.functions.values() { diff --git a/src/runtime.rs b/src/runtime.rs index 00352095..80759ede 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -54,7 +54,7 @@ pub struct Runtime { pub call_stack: Vec, pub local_stack: Vec<(Arc, usize)>, pub ret: Arc, - pub rng: rand::ThreadRng, + pub rng: rand::StdRng, pub text_type: Variable, pub f64_type: Variable, pub vec4_type: Variable, @@ -67,6 +67,7 @@ pub struct Runtime { pub rust_object_type: Variable, pub option_type: Variable, pub result_type: Variable, + pub thread_type: Variable, } fn resolve<'a>(stack: &'a Vec, var: &'a Variable) -> &'a Variable { @@ -202,7 +203,7 @@ impl Runtime { call_stack: vec![], local_stack: vec![], ret: Arc::new("return".into()), - rng: rand::thread_rng(), + rng: rand::StdRng::new().unwrap(), text_type: Variable::Text(Arc::new("string".into())), f64_type: Variable::Text(Arc::new("number".into())), vec4_type: Variable::Text(Arc::new("vec4".into())), @@ -215,6 +216,7 @@ impl Runtime { rust_object_type: Variable::Text(Arc::new("rust_object".into())), option_type: Variable::Text(Arc::new("option".into())), result_type: Variable::Text(Arc::new("result".into())), + thread_type: Variable::Text(Arc::new("thread".into())), } } @@ -352,6 +354,7 @@ impl Runtime { Break(ref b) => Ok((Expect::Nothing, Flow::Break(b.label.clone()))), Continue(ref b) => Ok((Expect::Nothing, Flow::ContinueLoop(b.label.clone()))), + Go(ref go) => self.go(go, module), Call(ref call) => self.call(call, module), Item(ref item) => { let flow = try!(self.item(item, side, module)); @@ -516,6 +519,68 @@ impl Runtime { Ok((expect, Flow::Continue)) } + pub fn go(&mut self, go: &ast::Go, module: &Module) -> Result<(Expect, Flow), String> { + use std::thread::{self, JoinHandle}; + use Thread; + + let n = go.call.args.len(); + let mut stack = vec![]; + let mut fake_call = ast::Call { + name: go.call.name.clone(), + args: Vec::with_capacity(n), + source_range: go.call.source_range, + }; + // Evaluate the arguments and put a deep clone on the new stack. + // This prevents the arguments from containing any reference to other variables. + for (i, arg) in go.call.args.iter().enumerate() { + match try!(self.expression(arg, Side::Right, module)) { + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + (Expect::Something, Flow::Continue) => {} + _ => return Err(module.error(arg.source_range(), + &format!("{}\nExpected something. \ + Expression did not return a value.", + self.stack_trace()))) + }; + let v = self.stack.pop().expect("There is no value on the stack"); + stack.push(v.deep_clone(&self.stack)); + fake_call.args.push(ast::Expression::Variable( + go.call.args[i].source_range(), Variable::Ref(n-i-1))); + } + stack.reverse(); + let new_rt = Runtime { + stack: stack, + local_stack: vec![], + call_stack: vec![], + rng: self.rng.clone(), + ret: self.ret.clone(), + ref_type: self.ref_type.clone(), + option_type: self.option_type.clone(), + array_type: self.array_type.clone(), + bool_type: self.bool_type.clone(), + object_type: self.object_type.clone(), + text_type: self.text_type.clone(), + f64_type: self.f64_type.clone(), + thread_type: self.thread_type.clone(), + unsafe_ref_type: self.unsafe_ref_type.clone(), + return_type: self.return_type.clone(), + rust_object_type: self.rust_object_type.clone(), + vec4_type: self.vec4_type.clone(), + result_type: self.result_type.clone(), + }; + let new_module: Module = module.clone(); + let handle: JoinHandle = thread::spawn(move || { + let mut new_rt = new_rt; + let new_module = new_module; + let fake_call = fake_call; + let _ = new_rt.call(&fake_call, &new_module); + + let v = new_rt.stack.pop().expect("There is no value on the stack"); + v.deep_clone(&new_rt.stack) + }); + self.stack.push(Variable::Thread(Thread::new(handle))); + Ok((Expect::Something, Flow::Continue)) + } + pub fn call( &mut self, call: &ast::Call, @@ -840,7 +905,7 @@ impl Runtime { } r } - x => panic!("Expected reference, found `{:?}`", x) + x => panic!("Expected reference, found `{}`", self.typeof_var(&x)) }; match self.resolve(&b) { @@ -1435,6 +1500,7 @@ impl Runtime { &Variable::RustObject(_) => self.rust_object_type.clone(), &Variable::Option(_) => self.option_type.clone(), &Variable::Result(_) => self.result_type.clone(), + &Variable::Thread(_) => self.thread_type.clone(), }; match v { Variable::Text(v) => v, diff --git a/tests/lib.rs b/tests/lib.rs index 1bf858a5..80e59f70 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -93,6 +93,7 @@ fn test_typechk() { test_fail_src("source/typechk/call.dyon"); test_src("source/typechk/obj.dyon"); test_fail_src("source/typechk/obj_2.dyon"); + test_fail_src("source/typechk/go.dyon"); } #[test] From 0c737686e6e7cae2a97f060fcc63968a87dc7169 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 14 May 2016 23:38:40 +0200 Subject: [PATCH 03/17] Return runtime error from other thread --- source/test.dyon | 53 ++++++++----------------------------------- src/intrinsics/mod.rs | 8 ++++++- src/lib.rs | 6 ++--- src/runtime.rs | 9 +++++--- 4 files changed, 25 insertions(+), 51 deletions(-) diff --git a/source/test.dyon b/source/test.dyon index ffbb85a1..56b0678e 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,48 +1,13 @@ -fn get_url(url) -> res { - data := load( - meta: "assets/test_meta.txt", - url: url - )? - return ok(data) +fn foo(a: 'b f64, mut b: []) -> { + b[0] = {a: a} + debug() + return 5 } -fn foo(n, a: [{}]) -> { - println(n) - println(a[0].x) - return sum i n { - sleep(0.1) - i+1 - } -} - -fn bar() {} - -fn bar2() -> f64 { - return 2 -} - -fn main() { - url := "https://goo.gl/IzuRJU" - - b := {x: "Hi!"} - a := [b] - - // data_thread := go get_url(url) - data_thread := go foo(4, a) - // data_thread := go bar() - - for i 10 { - sleep(0.1) - } - println("Done here") - data := unwrap(join(thread: data_thread)) - println(data) -} - -/* fn main() { - if go bar2() { - - } + y := 5 + x := [{}, 2, 3] + foo_thread := go foo(y, mut x) + r := unwrap(join(thread: foo_thread)) + println("r: " + to_string(r)) } -*/ diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 23eefbd9..55517d89 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1149,7 +1149,13 @@ pub fn call_standard( match handle_res { Ok(handle) => { match handle.join() { - Ok(res) => Ok(Box::new(res)), + Ok(res) => match res { + Ok(res) => Ok(Box::new(res)), + Err(err) => Err(Box::new(Error { + message: Variable::Text(Arc::new(err)), + trace: vec![] + })) + }, Err(_err) => Err(Box::new(Error { message: Variable::Text(Arc::new( "Thread did not exit successfully".into())), diff --git a/src/lib.rs b/src/lib.rs index 4d321c72..e122c530 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,11 +38,11 @@ pub struct Error { #[derive(Clone)] pub struct Thread { - pub handle: Option>>>, + pub handle: Option>>>>, } impl Thread { - pub fn new(handle: JoinHandle) -> Thread { + pub fn new(handle: JoinHandle>) -> Thread { Thread { handle: Some(Arc::new(Mutex::new(handle))) } @@ -53,7 +53,7 @@ impl Thread { pub fn invalidate_handle( rt: &mut Runtime, var: Variable - ) -> Result, String> { + ) -> Result>, String> { use std::error::Error; let thread = match var { diff --git a/src/runtime.rs b/src/runtime.rs index 80759ede..5337e617 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -568,14 +568,17 @@ impl Runtime { result_type: self.result_type.clone(), }; let new_module: Module = module.clone(); - let handle: JoinHandle = thread::spawn(move || { + let handle: JoinHandle> = thread::spawn(move || { let mut new_rt = new_rt; let new_module = new_module; let fake_call = fake_call; - let _ = new_rt.call(&fake_call, &new_module); + match new_rt.call(&fake_call, &new_module) { + Err(err) => return Err(err), + Ok(_) => {} + }; let v = new_rt.stack.pop().expect("There is no value on the stack"); - v.deep_clone(&new_rt.stack) + Ok(v.deep_clone(&new_rt.stack)) }); self.stack.push(Variable::Thread(Thread::new(handle))); Ok((Expect::Something, Flow::Continue)) From bd25eabc7b97bc8598eb2f35f3295c46d9ae966c Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 14 May 2016 23:52:23 +0200 Subject: [PATCH 04/17] Check that `go` functions does not have lifetime constraints --- source/syntax/lifetime_9.dyon | 13 +++++++++++++ src/lifetime/mod.rs | 36 +++++++++++++++++++++++++++++++++++ tests/lib.rs | 1 + 3 files changed, 50 insertions(+) create mode 100644 source/syntax/lifetime_9.dyon diff --git a/source/syntax/lifetime_9.dyon b/source/syntax/lifetime_9.dyon new file mode 100644 index 00000000..56b0678e --- /dev/null +++ b/source/syntax/lifetime_9.dyon @@ -0,0 +1,13 @@ +fn foo(a: 'b f64, mut b: []) -> { + b[0] = {a: a} + debug() + return 5 +} + +fn main() { + y := 5 + x := [{}, 2, 3] + foo_thread := go foo(y, mut x) + r := unwrap(join(thread: foo_thread)) + println("r: " + to_string(r)) +} diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index cb800663..f2919b6e 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -348,6 +348,42 @@ pub fn check( } } + // Check that `go` functions does not have lifetime constraints. + for &c in &calls { + let call = &nodes[c]; + if let Some(parent) = call.parent { + if nodes[parent].kind != Kind::Go { continue } + } else { + continue; + } + if let Some(declaration) = call.declaration { + let function = &nodes[declaration]; + for (i, &a) in function.children.iter() + .enumerate() + .filter(|&(_, &i)| nodes[i].kind == Kind::Arg) { + let arg = &nodes[a]; + if arg.lifetime.is_some() { + return Err(nodes[call.children[i]].source.wrap( + format!("Can not use `go` because this argument has a lifetime constraint"))); + } + } + } else { + // Check that call to intrinsic satisfy the declared constraints. + for ((i, <), _) in + call.lts.iter().enumerate() + .zip(call.children.iter() + .filter(|&&n| nodes[n].kind == Kind::CallArg)) { + match lt { + Lt::Default => {} + _ => { + return Err(nodes[call.children[i]].source.wrap( + format!("Can not use `go` because this argument has a lifetime constraint"))); + } + } + } + } + } + // Check that calls satisfy the lifetime constraints of arguments. for &c in &calls { let call = &nodes[c]; diff --git a/tests/lib.rs b/tests/lib.rs index 80e59f70..8a8f1542 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -54,6 +54,7 @@ fn test_syntax() { test_src("source/syntax/lifetime_6.dyon"); test_src("source/syntax/lifetime_7.dyon"); test_src("source/syntax/lifetime_8.dyon"); + test_fail_src("source/syntax/lifetime_9.dyon"); test_src("source/syntax/insert.dyon"); test_src("source/syntax/named_call.dyon"); test_src("source/syntax/max_min.dyon"); From eb285d1279ab581bb9dc3464f07f82a2fb1fae09 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 00:37:30 +0200 Subject: [PATCH 05/17] Print out "_thread" when printing variable This prevents panic when printing out a variable that has no valid input representation. --- source/test.dyon | 24 +++++++++++++++--------- src/intrinsics/mod.rs | 6 +++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/source/test.dyon b/source/test.dyon index 56b0678e..c4ee3f60 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,13 +1,19 @@ -fn foo(a: 'b f64, mut b: []) -> { - b[0] = {a: a} - debug() - return 5 +sum_n(i, n) = ∑ j n { i * n + j + 1 } + +fn results(mut threads) -> res { + return ok(∑ _ len(threads) { + join(thread: pop(mut threads))? + }) } fn main() { - y := 5 - x := [{}, 2, 3] - foo_thread := go foo(y, mut x) - r := unwrap(join(thread: foo_thread)) - println("r: " + to_string(r)) + n := 10000000 + + t := 10 + threads := sift i t { + go sum_n(i, n / t) + } + println(unwrap(results(mut threads))) + + // println(foo(0, n)) } diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 55517d89..baaba972 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -237,7 +237,11 @@ fn write_variable( } } } - ref x => panic!("Could not print out `{:?}`", x) + Variable::Thread(_) => try!(write!(w, "_thread")), + Variable::Return => try!(write!(w, "_return")), + Variable::UnsafeRef(_) => try!(write!(w, "_unsafe_ref")), + Variable::RustObject(_) => try!(write!(w, "_rust_object")), + // ref x => panic!("Could not print out `{:?}`", x) } Ok(()) } From a31b06f291c88270980f59467083170190185049 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 00:42:59 +0200 Subject: [PATCH 06/17] Added `go` keyword to Atom editor plugin --- editor-plugins/atom/grammars/dyon.cson | 2 +- source/test.dyon | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/editor-plugins/atom/grammars/dyon.cson b/editor-plugins/atom/grammars/dyon.cson index d2808369..d44692d9 100644 --- a/editor-plugins/atom/grammars/dyon.cson +++ b/editor-plugins/atom/grammars/dyon.cson @@ -140,7 +140,7 @@ { 'comment': 'Keyword' 'name': 'keyword.other.dyon' - 'match': '\\b(sum|min|max|any|all|sift|and|or)\\b' + 'match': '\\b(sum|min|max|any|all|sift|and|or|go)\\b' } { 'include': '#self' } { 'include': '#mut' } diff --git a/source/test.dyon b/source/test.dyon index c4ee3f60..0cfcc317 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -10,9 +10,7 @@ fn main() { n := 10000000 t := 10 - threads := sift i t { - go sum_n(i, n / t) - } + threads := sift i t { go sum_n(i, n / t) } println(unwrap(results(mut threads))) // println(foo(0, n)) From 64135d09546e38eaa7ffbfa2dd1ffcad7a3cbe2c Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 12:12:26 +0200 Subject: [PATCH 07/17] Added `Type::Thread` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/threads.dyon” - Infer thread type from function - Infer numbers from short loops - Make sift infer type from body - Removed non-existing typechk test --- assets/syntax.txt | 2 ++ source/test.dyon | 12 +++++++----- source/typechk/threads.dyon | 17 +++++++++++++++++ src/lifetime/typecheck.rs | 33 +++++++++++++++++++++++++++++---- src/typecheck.rs | 31 +++++++++++++++++++++++++++++++ tests/lib.rs | 8 ++++++-- 6 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 source/typechk/threads.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index 88880581..3430ff30 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -120,6 +120,8 @@ _seps: "(){}[],.:;=<>*·+-/%^?|&∧∨!¬∑∃∀\n" "[]":"arr_any" ["[" ?w type:"arr" ?w "]"] "{}":"obj_any" + ["thr" ?w "[" ?w type:"thr" ?w "]"] + "thr":"thr_any" } 101 + = [?w {"+":"+" "||":"+" "∨":"+" "or":"+"} ?w] diff --git a/source/test.dyon b/source/test.dyon index 0cfcc317..ca5ce298 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,17 +1,19 @@ -sum_n(i, n) = ∑ j n { i * n + j + 1 } +fn sum_n(i: f64, n: f64) -> f64 { + return ∑ j n { i * n + j + 1 } +} -fn results(mut threads) -> res { +fn results(mut threads: [f64]) -> res[f64] { return ok(∑ _ len(threads) { join(thread: pop(mut threads))? }) } fn main() { - n := 10000000 + n := 100 - t := 10 + t := 2 threads := sift i t { go sum_n(i, n / t) } println(unwrap(results(mut threads))) - // println(foo(0, n)) + // println(sum_n(0, n)) } diff --git a/source/typechk/threads.dyon b/source/typechk/threads.dyon new file mode 100644 index 00000000..dda742ef --- /dev/null +++ b/source/typechk/threads.dyon @@ -0,0 +1,17 @@ +fn sum_n(i: f64, n: f64) -> f64 { + return ∑ j n { i * n + j + 1 } +} + +fn results(mut threads: [f64]) -> res[f64] { + return ok(∑ _ len(threads) { + join(thread: pop(mut threads))? + }) +} + +fn main() { + n := 100 + + t := 2 + threads := sift i t { go sum_n(i, n / t) } + println(unwrap(results(mut threads))) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 546d1da8..e0abdde9 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -14,7 +14,13 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range let mut this_ty = None; match kind { Kind::Go => { - // TODO: Infer thread type from function. + // Infer thread type from function. + if nodes[i].children.len() > 0 { + let ch = nodes[i].children[0]; + if let Some(ref ty) = nodes[ch].ty { + this_ty = Some(Type::Thread(Box::new(ty.clone()))) + } + } } Kind::Call => { if let Some(decl) = nodes[i].declaration { @@ -88,9 +94,17 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range Kind::Item => { if nodes[i].item_try_or_ids() { continue 'node; } if let Some(decl) = nodes[i].declaration { - // No member identifiers. - if let Some(ref ty) = nodes[decl].ty { - this_ty = Some(ty.clone()); + match nodes[decl].kind { + Kind::Sum | Kind::Min | Kind::Max | + Kind::Any | Kind::All | Kind::Sift => { + // All indices are numbers. + this_ty = Some(Type::F64); + } + _ => { + if let Some(ref ty) = nodes[decl].ty { + this_ty = Some(ty.clone()); + } + } } } if let Some(parent) = nodes[i].parent { @@ -191,6 +205,17 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range } } } + Kind::Sift => { + // Infer type from body. + let ch = if let Some(ch) = nodes[i].find_child_by_kind(nodes, Kind::Block) { + ch + } else { + continue 'node; + }; + if let Some(ref ty) = nodes[ch].ty { + this_ty = Some(Type::Array(Box::new(ty.clone()))); + } + } _ => {} } if this_ty.is_some() { diff --git a/src/typecheck.rs b/src/typecheck.rs index 08a020b0..68bbd15f 100644 --- a/src/typecheck.rs +++ b/src/typecheck.rs @@ -16,6 +16,7 @@ pub enum Type { // Rust(Arc), Option(Box), Result(Box), + Thread(Box), } impl Type { @@ -60,6 +61,16 @@ impl Type { res } } + &Thread(ref ty) => { + if let Any = **ty { + "thr".into() + } else { + let mut res = String::from("thr["); + res.push_str(&ty.description()); + res.push(']'); + res + } + } } } @@ -79,6 +90,10 @@ impl Type { Type::Result(Box::new(Type::Any)) } + pub fn thread() -> Type { + Type::Thread(Box::new(Type::Any)) + } + pub fn goes_with(&self, other: &Type) -> bool { use self::Type::*; @@ -120,6 +135,15 @@ impl Type { false } } + &Thread(ref thr) => { + if let &Thread(ref other_thr) = other { + thr.goes_with(other_thr) + } else if let &Any = other { + true + } else { + false + } + } // Void, Bool, F64, Text, Vec4. x if x == other => { true } _ if *other == Type::Any => { true } @@ -214,6 +238,9 @@ impl Type { } else if let Ok((range, _)) = convert.meta_bool("obj_any") { convert.update(range); ty = Some(Type::Object); + } else if let Ok((range, _)) = convert.meta_bool("thr_any") { + convert.update(range); + ty = Some(Type::Thread(Box::new(Type::Any))); } else if let Ok((range, val)) = Type::from_meta_data( "opt", convert, ignored) { convert.update(range); @@ -226,6 +253,10 @@ impl Type { "arr", convert, ignored) { convert.update(range); ty = Some(Type::Array(Box::new(val))); + } else if let Ok((range, val)) = Type::from_meta_data( + "thr", convert, ignored) { + convert.update(range); + ty = Some(Type::Thread(Box::new(val))); } else { let range = convert.ignore(); convert.update(range); diff --git a/tests/lib.rs b/tests/lib.rs index 8a8f1542..6b75e327 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -14,7 +14,11 @@ pub fn test_fail_src(source: &str) { let mut module = Module::new(); match load(source, &mut module) { Ok(_) => panic!("`{}` should fail", source), - Err(_) => {} + Err(err) => { + if err.starts_with(&format!("Could not open `{}`", source)) { + panic!("{}", err) + } + } }; } @@ -93,8 +97,8 @@ fn test_typechk() { test_fail_src("source/typechk/pow_3.dyon"); test_fail_src("source/typechk/call.dyon"); test_src("source/typechk/obj.dyon"); - test_fail_src("source/typechk/obj_2.dyon"); test_fail_src("source/typechk/go.dyon"); + test_fail_src("source/typechk/threads.dyon"); } #[test] From 933dfc791cced3f131b43d823333cdb12bf1939b Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 12:15:21 +0200 Subject: [PATCH 08/17] Added `thr` as keyword to editor plugin --- editor-plugins/atom/grammars/dyon.cson | 2 +- source/test.dyon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor-plugins/atom/grammars/dyon.cson b/editor-plugins/atom/grammars/dyon.cson index d44692d9..f277ef8b 100644 --- a/editor-plugins/atom/grammars/dyon.cson +++ b/editor-plugins/atom/grammars/dyon.cson @@ -74,7 +74,7 @@ 'core_types': { 'comment': 'Built-in/core type' 'name': 'storage.type.core.dyon' - 'match': '\\b(bool|f64|vec4|str|opt|res)\\b' + 'match': '\\b(bool|f64|vec4|str|opt|res|thr)\\b' } 'core_vars': { 'comment': 'Core type variant' diff --git a/source/test.dyon b/source/test.dyon index ca5ce298..3cdaf337 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -2,7 +2,7 @@ fn sum_n(i: f64, n: f64) -> f64 { return ∑ j n { i * n + j + 1 } } -fn results(mut threads: [f64]) -> res[f64] { +fn results(mut threads: [thr[f64]]) -> res[f64] { return ok(∑ _ len(threads) { join(thread: pop(mut threads))? }) From e86b8c56487a401a3cb4de2f701af36d5f5aa4cc Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 12:18:41 +0200 Subject: [PATCH 09/17] Added info about alternative to restart --- editor-plugins/atom/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/editor-plugins/atom/README.md b/editor-plugins/atom/README.md index 6fa7020f..8c8875bb 100644 --- a/editor-plugins/atom/README.md +++ b/editor-plugins/atom/README.md @@ -10,3 +10,10 @@ Dyon syntax highlighting for the Atom editor 3. Create a new folder `~/.atom/packages/language-dyon` 4. Copy the content of `editor-plugins/atom/` into the new folder 5. Restart Atom + +Alternative to restart: + +1. In Atom, go to the menu `Packages -> Command Palette -> Toggle` +2. Type "Window: Reload" and hit Enter + +The window will flash for a moment and reload the packages. From 846484398bde45553c5244363616297819ebb32e Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 12:35:54 +0200 Subject: [PATCH 10/17] Fixed "call" return type - Made type checker show an error when result is unused --- source/test.dyon | 2 ++ src/intrinsics/mod.rs | 4 ++-- src/lifetime/typecheck.rs | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/test.dyon b/source/test.dyon index 3cdaf337..523bfa79 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -15,5 +15,7 @@ fn main() { threads := sift i t { go sum_n(i, n / t) } println(unwrap(results(mut threads))) + join(thread: go sum_n(0, 100)) + // println(sum_n(0, n)) } diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index baaba972..4a909421 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -99,7 +99,7 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { f.insert(Arc::new("call".into()), PreludeFunction { lts: vec![Lt::Default; 3], tys: vec![Type::Any, Type::Text, Type::array()], - ret: Type::Any + ret: Type::Void }); f.insert(Arc::new("call_ret".into()), PreludeFunction { lts: vec![Lt::Default; 3], @@ -137,7 +137,7 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { tys: vec![Type::Text; 2], ret: Type::Result(Box::new(Type::array())) }); - sarg(f, "join_thread", Type::Any, Type::Result(Box::new(Type::Any))); + sarg(f, "join_thread", Type::thread(), Type::Result(Box::new(Type::Any))); } enum EscapeString { diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index e0abdde9..36471822 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -257,6 +257,20 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range Kind::If => { try!(check_if(i, nodes)) } + Kind::Block => { + // Make sure all results are used. + if nodes[i].children.len() <= 1 { continue } + for j in 0..nodes[i].children.len() - 1 { + let ch = nodes[i].children[j]; + if let Some(ref ty) = nodes[ch].ty { + if ty != &Type::Void { + return Err(nodes[ch].source.wrap( + format!("Unused result `{}`", ty.description()) + )); + } + } + } + } _ => {} } } From 91a299a04f520debecf828a373d12c34262715ca Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 14:05:07 +0200 Subject: [PATCH 11/17] Separate type check on call arguments from the return type of call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/unused_result.dyon” - Fixed “random” intrinsic --- source/test.dyon | 12 ++++++ source/typechk/unused_result.dyon | 8 ++++ src/intrinsics/mod.rs | 2 +- src/lifetime/typecheck.rs | 69 +++++++++++++++---------------- tests/lib.rs | 1 + 5 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 source/typechk/unused_result.dyon diff --git a/source/test.dyon b/source/test.dyon index 523bfa79..b4793aae 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,3 +1,4 @@ +/* fn sum_n(i: f64, n: f64) -> f64 { return ∑ j n { i * n + j + 1 } } @@ -18,4 +19,15 @@ fn main() { join(thread: go sum_n(0, 100)) // println(sum_n(0, n)) + // println("hi") +} +*/ + +fn foo() -> f64 { + 3 + 4 + return 7 +} + +fn main() { + println(foo()) } diff --git a/source/typechk/unused_result.dyon b/source/typechk/unused_result.dyon new file mode 100644 index 00000000..8879129f --- /dev/null +++ b/source/typechk/unused_result.dyon @@ -0,0 +1,8 @@ +fn foo() -> f64 { + 3 + 4 + return 7 +} + +fn main() { + println(foo()) +} diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 4a909421..b4c4d45b 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -43,7 +43,7 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { f.insert(Arc::new("random".into()), PreludeFunction { lts: vec![], tys: vec![], - ret: Type::Void + ret: Type::F64 }); sarg(f, "read_number", Type::Text, Type::Any); f.insert(Arc::new("read_line".into()), PreludeFunction { diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 36471822..fefb9c0c 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -22,53 +22,48 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range } } } - Kind::Call => { - if let Some(decl) = nodes[i].declaration { - let mut missing = false; - for j in 0..nodes[i].children.len() { - let ch = nodes[i].children[j]; - if nodes[ch].item_try_or_ids() { continue 'node; } + Kind::CallArg => { + if nodes[i].children.len() == 0 || nodes[i].item_try_or_ids() { + continue 'node; + } + let expr_type = nodes[nodes[i].children[0]].ty.clone(); + if let Some(parent) = nodes[i].parent { + let j = nodes[parent].children.iter().position(|&ch| ch == i); + let j = if let Some(j) = j { j } else { continue 'node }; + if let Some(decl) = nodes[parent].declaration { let arg = nodes[decl].children[j]; - match (&nodes[ch].ty, &nodes[arg].ty) { + match (&expr_type, &nodes[arg].ty) { (&Some(ref ch_ty), &Some(ref arg_ty)) => { if !ch_ty.goes_with(arg_ty) { - return Err(nodes[ch].source.wrap( + return Err(nodes[i].source.wrap( format!("Type mismatch: Expected `{}`, found `{}`", arg_ty.description(), ch_ty.description()))); } } - (&None, _) | (_, &None) => { - missing = true; - } + (&None, _) | (_, &None) => {} } - } - if !missing { - if let Some(ref ty) = nodes[decl].ty { - this_ty = Some(ty.clone()); + } else if let Some(ref f) = prelude.functions.get( + nodes[parent].name.as_ref().unwrap()) { + if let Some(ref ty) = expr_type { + if !ty.goes_with(&f.tys[j]) { + return Err(nodes[i].source.wrap( + format!("Type mismatch: Expected `{}`, found `{}`", + f.tys[j].description(), ty.description()) + )) + } } } } - if this_ty.is_none() { - if let Some(ref f) = prelude.functions.get( - nodes[i].name.as_ref().unwrap()) { - let mut missing = false; - for j in 0..nodes[i].children.len() { - let ch = nodes[i].children[j]; - if nodes[ch].item_try_or_ids() { continue 'node; } - if let &Some(ref ch_ty) = &nodes[ch].ty { - if !ch_ty.goes_with(&f.tys[j]) { - return Err(nodes[ch].source.wrap( - format!("Type mismatch: Expected `{}`, found `{}`", - f.tys[j].description(), ch_ty.description()))); - } - } else { - missing = true; - } - } - if !missing { - this_ty = Some(f.ret.clone()); - } + this_ty = expr_type; + } + Kind::Call => { + if let Some(decl) = nodes[i].declaration { + if let Some(ref ty) = nodes[decl].ty { + this_ty = Some(ty.clone()); } + } else if let Some(ref f) = prelude.functions.get( + nodes[i].name.as_ref().unwrap()) { + this_ty = Some(f.ret.clone()); } } Kind::Assign => { @@ -116,7 +111,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range } } } - Kind::Return | Kind::Val | Kind::CallArg | Kind::Expr + Kind::Return | Kind::Val | Kind::Expr | Kind::Cond | Kind::Exp | Kind::Base | Kind::Right => { for &ch in &nodes[i].children { if nodes[ch].item_try_or_ids() { continue 'node; } @@ -258,6 +253,8 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range try!(check_if(i, nodes)) } Kind::Block => { + // TODO: If the block is the body of a function or for loop, + // then the last child node should be checked too. // Make sure all results are used. if nodes[i].children.len() <= 1 { continue } for j in 0..nodes[i].children.len() - 1 { diff --git a/tests/lib.rs b/tests/lib.rs index 6b75e327..7610fbfb 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -99,6 +99,7 @@ fn test_typechk() { test_src("source/typechk/obj.dyon"); test_fail_src("source/typechk/go.dyon"); test_fail_src("source/typechk/threads.dyon"); + test_fail_src("source/typechk/unused_result.dyon"); } #[test] From 9b0a559bdcbb7a08172aab8afaa533d5d5ee1a6f Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 14:31:29 +0200 Subject: [PATCH 12/17] =?UTF-8?q?-=20Fixed=20=E2=80=9Csyntax/new=5Fpos.dyo?= =?UTF-8?q?n=E2=80=9D=20-=20Added=20=E2=80=9Ctypechk/unused=5Fresult=5F2.d?= =?UTF-8?q?yon=E2=80=9D=20-=20Check=20type=20of=20only=20one=20child=20for?= =?UTF-8?q?=20some=20nodes=20-=20Check=20all=20expressions=20in=20body=20o?= =?UTF-8?q?f=20a=20functions=20that=20does=20not=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/syntax/new_pos.dyon | 2 +- source/test.dyon | 12 +-------- source/typechk/unused_result_2.dyon | 7 +++++ src/lifetime/typecheck.rs | 42 +++++++++++++++++++++-------- tests/lib.rs | 1 + 5 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 source/typechk/unused_result_2.dyon diff --git a/source/syntax/new_pos.dyon b/source/syntax/new_pos.dyon index 6d1ce5a9..6c968dbf 100644 --- a/source/syntax/new_pos.dyon +++ b/source/syntax/new_pos.dyon @@ -1,3 +1,3 @@ fn new() { - {x: 0, y: 0} + _ := {x: 0, y: 0} } diff --git a/source/test.dyon b/source/test.dyon index b4793aae..51791ac8 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,4 +1,4 @@ -/* + fn sum_n(i: f64, n: f64) -> f64 { return ∑ j n { i * n + j + 1 } } @@ -21,13 +21,3 @@ fn main() { // println(sum_n(0, n)) // println("hi") } -*/ - -fn foo() -> f64 { - 3 + 4 - return 7 -} - -fn main() { - println(foo()) -} diff --git a/source/typechk/unused_result_2.dyon b/source/typechk/unused_result_2.dyon new file mode 100644 index 00000000..5191bd4f --- /dev/null +++ b/source/typechk/unused_result_2.dyon @@ -0,0 +1,7 @@ +fn foo() { + 3 + 4 +} + +fn main() { + foo() +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index fefb9c0c..ceffeb9a 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -113,12 +113,13 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range } Kind::Return | Kind::Val | Kind::Expr | Kind::Cond | Kind::Exp | Kind::Base | Kind::Right => { - for &ch in &nodes[i].children { - if nodes[ch].item_try_or_ids() { continue 'node; } - if let Some(ref ty) = nodes[ch].ty { - this_ty = Some(ty.clone()); - break; - } + if nodes[i].children.len() == 0 { continue 'node; } + let ch = nodes[i].children[0]; + if nodes[ch].item_try_or_ids() { continue 'node; } + if nodes[ch].kind == Kind::Return { + this_ty = Some(Type::Void); + } else if let Some(ref ty) = nodes[ch].ty { + this_ty = Some(ty.clone()); } } Kind::Add => { @@ -253,12 +254,31 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range try!(check_if(i, nodes)) } Kind::Block => { - // TODO: If the block is the body of a function or for loop, - // then the last child node should be checked too. // Make sure all results are used. - if nodes[i].children.len() <= 1 { continue } - for j in 0..nodes[i].children.len() - 1 { - let ch = nodes[i].children[j]; + // TODO: If the block is the body of a for loop, + // then the last child node should be checked too. + let n = nodes[i].children.len(); + if n == 0 { continue; } + let children = if let Some(parent) = nodes[i].parent { + match nodes[parent].kind { + Kind::Fn => { + match &nodes[parent].ty { + &Some(Type::Void) => &nodes[i].children, + &None => continue, + _ => &nodes[i].children[0..n - 1] + } + } + _ => &nodes[i].children[0..n - 1] + } + } else { + &nodes[i].children[0..n - 1] + }; + for j in 0..children.len() { + let ch = children[j]; + match nodes[ch].kind { + Kind::Return => continue, + _ => {} + }; if let Some(ref ty) = nodes[ch].ty { if ty != &Type::Void { return Err(nodes[ch].source.wrap( diff --git a/tests/lib.rs b/tests/lib.rs index 7610fbfb..3886b2f4 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -100,6 +100,7 @@ fn test_typechk() { test_fail_src("source/typechk/go.dyon"); test_fail_src("source/typechk/threads.dyon"); test_fail_src("source/typechk/unused_result.dyon"); + test_fail_src("source/typechk/unused_result_2.dyon"); } #[test] From dade173f6068ec09a9fa88e6a5b27c52e66be977 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 15:19:09 +0200 Subject: [PATCH 13/17] Infer return type from body Infer return type from body of functions that are declared as mathematical expressions. - Clean up `Type::Void` usage --- assets/syntax.txt | 5 ++++- source/test.dyon | 7 ++----- src/ast.rs | 13 ++++++------- src/lifetime/node.rs | 16 ++++++---------- src/lifetime/typecheck.rs | 11 ++++++++++- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index 3430ff30..949f2e7c 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -7,7 +7,10 @@ _seps: "(){}[],.:;=<>*·+-/%^?|&∧∨!¬∑∃∀\n" 202 w = .r!({.w! comment}) 0 fn = { - ["fn" .w! .."("!:"name" ?w "(" ?w args ?w ")" ?w ?["->":"returns" ?w ?type:"ret_type"] ?w block:"block"] + ["fn" .w! .."("!:"name" ?w "(" ?w args ?w ")" ?w { + ["->":"returns" ?w ?type:"ret_type"] + !"->":!"returns" + } ?w block:"block"] [.."("!:"name" ?w "(" ?w args ?w ")" ?w "=" ?w expr:"expr"] } 1 args = .s?.(, arg:"arg") diff --git a/source/test.dyon b/source/test.dyon index 51791ac8..4ab7aaf1 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,7 +1,4 @@ - -fn sum_n(i: f64, n: f64) -> f64 { - return ∑ j n { i * n + j + 1 } -} +sum_n(i: f64, n: f64) = ∑ j n { i * n + j + 1 } fn results(mut threads: [thr[f64]]) -> res[f64] { return ok(∑ _ len(threads) { @@ -16,7 +13,7 @@ fn main() { threads := sift i t { go sum_n(i, n / t) } println(unwrap(results(mut threads))) - join(thread: go sum_n(0, 100)) + println(join(thread: go sum_n(0, 100))) // println(sum_n(0, n)) // println("hi") diff --git a/src/ast.rs b/src/ast.rs index e7500512..6944bbb4 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -53,7 +53,7 @@ impl Function { let mut args: Vec = vec![]; let mut block: Option = None; let mut expr: Option = None; - let mut ret = Type::Void; + let mut ret: Option = None; loop { if let Ok(range) = convert.end_node(node) { convert.update(range); @@ -65,15 +65,13 @@ impl Function { convert, ignored) { convert.update(range); args.push(val); - } else if let Ok((range, _val)) = convert.meta_bool("returns") { + } else if let Ok((range, val)) = convert.meta_bool("returns") { convert.update(range); - if let Type::Void = ret { - ret = Type::Any; - } + ret = Some(if val { Type::Any } else { Type::Void }) } else if let Ok((range, val)) = Type::from_meta_data( "ret_type", convert, ignored) { convert.update(range); - ret = val; + ret = Some(val); } else if let Ok((range, val)) = Block::from_meta_data( "block", convert, ignored) { convert.update(range); @@ -82,6 +80,7 @@ impl Function { "expr", convert, ignored) { convert.update(range); expr = Some(val); + ret = Some(Type::Any); } else { let range = convert.ignore(); convert.update(range); @@ -93,7 +92,6 @@ impl Function { let block = match expr { None => try!(block.ok_or(())), Some(expr) => { - ret = Type::Any; let source_range = expr.source_range(); Block { expressions: vec![Expression::Return(Box::new(expr))], @@ -114,6 +112,7 @@ impl Function { name_plus_args.push(')'); name = Arc::new(name_plus_args); } + let ret = try!(ret.ok_or(())); Ok((convert.subtract(start), Function { name: name, file: file, diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 80e96482..dd284747 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -256,19 +256,11 @@ pub fn convert_meta_data( } } - if kind == Kind::Expr { - let parent = *parents.last().unwrap(); - if nodes[parent].kind == Kind::Fn { - // Function returns a value. - nodes[parent].ty = Some(Type::Any); - } - } - let ty = match kind { - Kind::Fn => Some(Type::Void), Kind::Array | Kind::ArrayFill => Some(Type::array()), Kind::Vec4 => Some(Type::Vec4), Kind::Object => Some(Type::object()), + Kind::Sum => Some(Type::F64), _ => None }; @@ -365,7 +357,11 @@ pub fn convert_meta_data( // Assuming this will be overwritten when // type is parsed or inferred. let i = *parents.last().unwrap(); - nodes[i].ty = Some(Type::Any); + if _val { + nodes[i].ty = Some(Type::Any); + } else { + nodes[i].ty = Some(Type::Void); + } } "return_void" => { // There is no sub node, so we need change kind of parent. diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index ceffeb9a..2a185162 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -22,6 +22,12 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range } } } + Kind::Fn => { + if let Some(ch) = nodes[i].find_child_by_kind(nodes, Kind::Expr) { + // Infer return type from body of function. + this_ty = nodes[ch].ty.clone(); + } + } Kind::CallArg => { if nodes[i].children.len() == 0 || nodes[i].item_try_or_ids() { continue 'node; @@ -95,6 +101,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range // All indices are numbers. this_ty = Some(Type::F64); } + Kind::Arg => { + this_ty = Some(nodes[decl].ty.as_ref() + .unwrap_or(&Type::Any).clone()); + } _ => { if let Some(ref ty) = nodes[decl].ty { this_ty = Some(ty.clone()); @@ -225,7 +235,6 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude) -> Result<(), Range let kind = nodes[i].kind; match kind { Kind::Fn => { - // TODO: Infer type from body when written as mathematical expression. if let Some(ref ty) = nodes[i].ty { try!(check_fn(i, nodes, ty)) } else { From 05efe28cdd4ebb2004a115ecb0594d0de82248db Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 15:47:51 +0200 Subject: [PATCH 14/17] Copy refined returned types The refined returned types inferred by the type checker should be copied to the AST for more accurate type checking when calling loaded function declared as mathematical expressions. --- src/lib.rs | 8 +++++++- src/lifetime/mod.rs | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e122c530..5d4d39fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,7 +258,13 @@ pub fn load(source: &str, module: &mut Module) -> Result<(), String> { // Check that lifetime checking succeeded. match handle.join().unwrap() { - Ok(()) => {} + Ok(refined_rets) => { + for (name, ty) in &refined_rets { + module.functions.get_mut(name).map(|f| { + Arc::make_mut(f).ret = ty.clone(); + }); + } + } Err(err_msg) => { let (range, msg) = err_msg.decouple(); return Err(format!("In `{}`:\n{}", source, module.error(range, &msg))) diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index f2919b6e..36dc4d12 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -11,15 +11,19 @@ use self::lt::{arg_lifetime, compare_lifetimes, Lifetime}; use prelude::{Lt, Prelude}; +use Type; + mod kind; pub mod node; mod lt; mod typecheck; +/// Checks lifetime constraints and does type checking. +/// Returns refined return types of functions to put in AST. pub fn check( data: &[Range], prelude: &Prelude -) -> Result<(), Range> { +) -> Result, Type>, Range> { let mut nodes: Vec = vec![]; try!(convert_meta_data(&mut nodes, data)); @@ -522,7 +526,15 @@ pub fn check( try!(typecheck::run(&mut nodes, prelude)); - Ok(()) + // Copy refined return types to use in AST. + let mut refined_rets: HashMap, Type> = HashMap::new(); + for (name, &ind) in &function_lookup { + if let Some(ref ty) = nodes[functions[ind]].ty { + refined_rets.insert(name.clone(), ty.clone()); + } + } + + Ok(refined_rets) } // Search for suggestions using matching function signature. From 9dff311c245e205d2434dad0fa02df22d881c9b7 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 16:24:36 +0200 Subject: [PATCH 15/17] Made "functions" intrinsic include type information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed “returns” to a string representing the type - Added “takes” for each argument representing the type --- interactive/examples/common/functions.dyon | 24 +++++++++++++------ .../examples/piston_window/loader.dyon | 10 +------- interactive/examples/piston_window/snake.dyon | 14 +++++++++++ source/functions/functions.dyon | 17 ++++++++++--- src/intrinsics/mod.rs | 13 +++++++--- 5 files changed, 56 insertions(+), 22 deletions(-) diff --git a/interactive/examples/common/functions.dyon b/interactive/examples/common/functions.dyon index 0da9a861..2f59f083 100644 --- a/interactive/examples/common/functions.dyon +++ b/interactive/examples/common/functions.dyon @@ -2,26 +2,36 @@ fn print_function(f: {}) { print(f.name) print("(") n := len(f.arguments) - for i := 0; i < n; i += 1 { + for i n { print(f.arguments[i].name) if f.arguments[i].lifetime != none() { print(": '" + unwrap(f.arguments[i].lifetime)) + if f.arguments[i].takes != "any" { + print(" " + f.argument[i].takes) + } + } else { + if f.arguments[i].takes != "any" { + print(": " + f.arguments[i].takes) + } } if (i + 1) < n { print(", ") } } print(")") - if f.returns { - print(" ->") + if f.returns != "void" { + if f.returns == "any" { + print(" ->") + } else { + print(" -> " + f.returns) + } } println("") } -fn print_functions(functions: [{}]) { - n := len(functions) - for i := 0; i < n; i += 1 { - if functions[i].type != "external" { continue } +fn print_functions_type(functions: [{}], type) { + for i len(functions) { + if functions[i].type != type { continue } print_function(functions[i]) } } diff --git a/interactive/examples/piston_window/loader.dyon b/interactive/examples/piston_window/loader.dyon index 77a5aa3d..3581376a 100644 --- a/interactive/examples/piston_window/loader.dyon +++ b/interactive/examples/piston_window/loader.dyon @@ -4,18 +4,10 @@ fn main() { println("Reset with R.") println("You can modify \"source/piston_window/snake.rs\" while running.") - { - println("External functions:") - println("===================") - functions := unwrap(load("examples/common/functions.dyon")) - list := functions() - call(functions, "print_functions", [list]) - println("===================") - } - render := unwrap(load("examples/piston_window/render.dyon")) source := "examples/piston_window/snake.dyon" m := unwrap(load(source: source, imports: [render])) + call(m, "init", []) settings := call_ret(m, "settings", []) data := call_ret(m, "init_data", [settings]) diff --git a/interactive/examples/piston_window/snake.dyon b/interactive/examples/piston_window/snake.dyon index 8fccf690..f55af209 100644 --- a/interactive/examples/piston_window/snake.dyon +++ b/interactive/examples/piston_window/snake.dyon @@ -1,5 +1,19 @@ title() = "Snake!" +fn init() { + functions := unwrap(load("examples/common/functions.dyon")) + list := functions() + println("===================") + println("External functions:") + println("===================") + call(functions, "print_functions_type", [list, "external"]) + println("===================") + println("Loaded functions:") + println("===================") + call(functions, "print_functions_type", [list, "loaded"]) + println("===================") +} + settings() = { background_color: #e5d6a1, reload_interval: 0.25, diff --git a/source/functions/functions.dyon b/source/functions/functions.dyon index 33e10eec..d4bb9717 100644 --- a/source/functions/functions.dyon +++ b/source/functions/functions.dyon @@ -10,18 +10,29 @@ fn print_function(f) { print(f.name) print("(") n := len(f.arguments) - for i := 0; i < n; i += 1 { + for i n { print(f.arguments[i].name) if f.arguments[i].lifetime != none() { print(": '" + unwrap(f.arguments[i].lifetime)) + if f.arguments[i].takes != "any" { + print(" " + f.argument[i].takes) + } + } else { + if f.arguments[i].takes != "any" { + print(": " + f.arguments[i].takes) + } } if (i + 1) < n { print(", ") } } print(")") - if f.returns { - print(" ->") + if f.returns != "void" { + if f.returns == "any" { + print(" ->") + } else { + print(" -> " + f.returns) + } } print(" ... ") print(f.type) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index b4c4d45b..078ef834 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -881,6 +881,7 @@ pub fn call_standard( let name: Arc = Arc::new("name".into()); let arguments: Arc = Arc::new("arguments".into()); let returns: Arc = Arc::new("returns".into()); + let takes: Arc = Arc::new("takes".into()); let lifetime: Arc = Arc::new("lifetime".into()); let ret_lifetime: Arc = Arc::new("return".into()); let ty: Arc = Arc::new("type".into()); @@ -892,7 +893,7 @@ pub fn call_standard( for (f_name, f) in &intrinsics { let mut obj = HashMap::new(); obj.insert(name.clone(), Variable::Text(f_name.clone())); - obj.insert(returns.clone(), Variable::Bool(f.returns())); + obj.insert(returns.clone(), Variable::Text(Arc::new(f.ret.description()))); obj.insert(ty.clone(), Variable::Text(intrinsic.clone())); let mut args = vec![]; for (i, lt) in f.lts.iter().enumerate() { @@ -910,6 +911,8 @@ pub fn call_standard( Box::new(Variable::Text(ret_lifetime.clone())) )), }); + obj_arg.insert(takes.clone(), + Variable::Text(Arc::new(f.tys[i].description()))); args.push(Variable::Object(Arc::new(obj_arg))); } obj.insert(arguments.clone(), Variable::Array(Arc::new(args))); @@ -918,7 +921,7 @@ pub fn call_standard( for (f_name, &(_, ref f)) in &*module.ext_prelude { let mut obj = HashMap::new(); obj.insert(name.clone(), Variable::Text(f_name.clone())); - obj.insert(returns.clone(), Variable::Bool(f.returns())); + obj.insert(returns.clone(), Variable::Text(Arc::new(f.ret.description()))); obj.insert(ty.clone(), Variable::Text(external.clone())); let mut args = vec![]; for (i, lt) in f.lts.iter().enumerate() { @@ -936,6 +939,8 @@ pub fn call_standard( Box::new(Variable::Text(ret_lifetime.clone())) )), }); + obj_arg.insert(takes.clone(), + Variable::Text(Arc::new(f.tys[i].description()))); args.push(Variable::Object(Arc::new(obj_arg))); } obj.insert(arguments.clone(), Variable::Array(Arc::new(args))); @@ -944,7 +949,7 @@ pub fn call_standard( for f in module.functions.values() { let mut obj = HashMap::new(); obj.insert(name.clone(), Variable::Text(f.name.clone())); - obj.insert(returns.clone(), Variable::Bool(f.returns())); + obj.insert(returns.clone(), Variable::Text(Arc::new(f.ret.description()))); obj.insert(ty.clone(), Variable::Text(loaded.clone())); let mut args = vec![]; for arg in &f.args { @@ -959,6 +964,8 @@ pub fn call_standard( ))) } ); + obj_arg.insert(takes.clone(), + Variable::Text(Arc::new(arg.ty.description()))); args.push(Variable::Object(Arc::new(obj_arg))); } obj.insert(arguments.clone(), Variable::Array(Arc::new(args))); From 9cbb5ffdead5196094aff0eed44f6eae8b963f64 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 16:33:16 +0200 Subject: [PATCH 16/17] Added thread benchmarks --- source/bench/threads_go.dyon | 15 +++++++++++++++ source/bench/threads_no_go.dyon | 6 ++++++ source/test.dyon | 19 +++---------------- src/lib.rs | 10 ++++++++++ 4 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 source/bench/threads_go.dyon create mode 100644 source/bench/threads_no_go.dyon diff --git a/source/bench/threads_go.dyon b/source/bench/threads_go.dyon new file mode 100644 index 00000000..21cda893 --- /dev/null +++ b/source/bench/threads_go.dyon @@ -0,0 +1,15 @@ +sum_n(i: f64, n: f64) = ∑ j n { i * n + j + 1 } + +fn results(mut threads: [thr[f64]]) -> res[f64] { + return ok(∑ _ len(threads) { + join(thread: pop(mut threads))? + }) +} + +fn main() { + n := 100_000 + + t := 2 + threads := sift i t { go sum_n(i, n / t) } + x := unwrap(results(mut threads)) +} diff --git a/source/bench/threads_no_go.dyon b/source/bench/threads_no_go.dyon new file mode 100644 index 00000000..41b98acf --- /dev/null +++ b/source/bench/threads_no_go.dyon @@ -0,0 +1,6 @@ +sum_n(i: f64, n: f64) = ∑ j n { i * n + j + 1 } + +fn main() { + n := 100_000 + x := sum_n(0, n) +} diff --git a/source/test.dyon b/source/test.dyon index 4ab7aaf1..f6bb2bdb 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,20 +1,7 @@ sum_n(i: f64, n: f64) = ∑ j n { i * n + j + 1 } -fn results(mut threads: [thr[f64]]) -> res[f64] { - return ok(∑ _ len(threads) { - join(thread: pop(mut threads))? - }) -} - fn main() { - n := 100 - - t := 2 - threads := sift i t { go sum_n(i, n / t) } - println(unwrap(results(mut threads))) - - println(join(thread: go sum_n(0, 100))) - - // println(sum_n(0, n)) - // println("hi") + n := 100_000 + x := sum_n(0, n) + println(x) } diff --git a/src/lib.rs b/src/lib.rs index 5d4d39fb..b7a05ad2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -375,4 +375,14 @@ mod tests { fn bench_primes_trad(b: &mut Bencher) { b.iter(|| run_bench("source/bench/primes_trad.dyon")); } + + #[bench] + fn bench_threads_no_go(b: &mut Bencher) { + b.iter(|| run_bench("source/bench/threads_no_go.dyon")); + } + + #[bench] + fn bench_threads_go(b: &mut Bencher) { + b.iter(|| run_bench("source/bench/threads_go.dyon")); + } } From 6bd0312b4fc1291e6dc5c7c4f6a2347f9b025827 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 May 2016 17:04:25 +0200 Subject: [PATCH 17/17] Added "download_url_file" intrinsic --- source/test.dyon | 20 +++++++++++++++----- src/intrinsics/meta.rs | 29 +++++++++++++++++++++++++++++ src/intrinsics/mod.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/source/test.dyon b/source/test.dyon index f6bb2bdb..3b617876 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,7 +1,17 @@ -sum_n(i: f64, n: f64) = ∑ j n { i * n + j + 1 } - fn main() { - n := 100_000 - x := sum_n(0, n) - println(x) + list := [ + "https://raw.githubusercontent.com/PistonDevelopers/dyon/master/assets/test_data.txt", + "https://raw.githubusercontent.com/PistonDevelopers/dyon/master/assets/test_meta.txt" + ] + names := [ + "1.txt", + "2.txt" + ] + threads := sift i len(list) { + go download(url: list[i], file: names[i]) + } + results := sift i len(threads) { + unwrap(join(thread: pop(mut threads))) + } + println(results) } diff --git a/src/intrinsics/meta.rs b/src/intrinsics/meta.rs index 2ab14a41..ff05a2d2 100644 --- a/src/intrinsics/meta.rs +++ b/src/intrinsics/meta.rs @@ -105,3 +105,32 @@ pub fn load_meta_url(meta: &str, url: &str) -> Result, String> { let d = try!(load_text_file_from_url(url)); load_metarules_data(meta, &s, url, &d) } + +// Downloads a file from url. +pub fn download_url_to_file(url: &str, file: &str) -> Result { + use hyper::client::Client; + use hyper::{Url}; + use hyper::status::StatusCode; + use std::io::copy; + use std::fs::File; + + let url_address = try!(Url::parse(url) + .map_err(|e| format!("Error parsing url:\n`{}`\n", e))); + let client = Client::new(); + let request = client.get(url_address); + let mut response = try!(request.send() + .map_err(|e| format!("Error fetching file over http `{}`:\n{}\n", + url, e.to_string()))); + if response.status == StatusCode::Ok { + let mut f = try!(File::create(file).map_err(|err| { + format!("Could not create file `{}`:\n{}", file, err.description()) + })); + try!(copy(&mut response, &mut f) + .map_err(|e| format!("Error fetching file over http `{}`:\n{}\n", + url, e.to_string()))); + Ok(file.into()) + } else { + Err(format!("Error fetching file over http `{}:\n{}\n", + url, response.status)) + } +} diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 078ef834..87189345 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -137,6 +137,11 @@ pub fn standard(f: &mut HashMap, PreludeFunction>) { tys: vec![Type::Text; 2], ret: Type::Result(Box::new(Type::array())) }); + f.insert(Arc::new("download_url_file".into()), PreludeFunction { + lts: vec![Lt::Default; 2], + tys: vec![Type::Text; 2], + ret: Type::Result(Box::new(Type::Text)) + }); sarg(f, "join_thread", Type::thread(), Type::Result(Box::new(Type::Any))); } @@ -1150,6 +1155,32 @@ pub fn call_standard( rt.pop_fn(call.name.clone()); Expect::Something } + "download_url_file" => { + rt.push_fn(call.name.clone(), None, st + 1, lc); + let file = rt.stack.pop().expect(TINVOTS); + let url = rt.stack.pop().expect(TINVOTS); + let file = match rt.resolve(&file) { + &Variable::Text(ref file) => file.clone(), + x => return Err(module.error(call.args[1].source_range(), + &rt.expected(x, "str"))) + }; + let url = match rt.resolve(&url) { + &Variable::Text(ref url) => url.clone(), + x => return Err(module.error(call.args[0].source_range(), + &rt.expected(x, "str"))) + }; + + let res = meta::download_url_to_file(&**url, &**file); + rt.stack.push(Variable::Result(match res { + Ok(res) => Ok(Box::new(Variable::Text(Arc::new(res)))), + Err(err) => Err(Box::new(Error { + message: Variable::Text(Arc::new(err)), + trace: vec![] + })) + })); + rt.pop_fn(call.name.clone()); + Expect::Something + } "join_thread" => { use Thread;