├── .gitignore ├── modules └── example │ ├── src │ └── example.burn │ └── burn_module.json ├── src ├── system_tests │ ├── statements │ │ ├── print.burntest │ │ ├── else.burntest │ │ ├── if.burntest │ │ ├── while.burntest │ │ └── else_if.burntest │ ├── modules │ │ └── module.burntest │ ├── literals │ │ ├── string.burntest │ │ ├── float.burntest │ │ └── integer.burntest │ ├── variables │ │ ├── variable.burntest │ │ ├── duplicate_declaration.burntest │ │ └── use_before_declaration.burntest │ ├── expressions │ │ ├── call.burntest │ │ ├── call_with_arguments.burntest │ │ ├── add.burntest │ │ └── subtract.burntest │ ├── types │ │ └── union.burntest │ ├── scoping │ │ ├── bind.burntest │ │ ├── block.burntest │ │ ├── nested_bind.burntest │ │ ├── bound_assignment.burntest │ │ ├── loop_bind_static.burntest │ │ └── loop_bind.burntest │ ├── throwing │ │ ├── finally.burntest │ │ ├── throwing_finally.burntest │ │ ├── bad_catch_type.burntest │ │ ├── try_in_finally.burntest │ │ ├── nested_finally.burntest │ │ ├── catch.burntest │ │ └── else.burntest │ ├── implicit │ │ └── types.burntest │ └── run_tests.py ├── libburn │ ├── vm │ │ ├── run │ │ │ ├── rust.rs │ │ │ ├── flow.rs │ │ │ ├── fiber.rs │ │ │ ├── frame.rs │ │ │ └── cpu.rs │ │ ├── repl.rs │ │ ├── error.rs │ │ ├── bytecode │ │ │ ├── code.rs │ │ │ ├── opcode.rs │ │ │ └── compiler.rs │ │ ├── analysis │ │ │ ├── annotation.rs │ │ │ ├── allocation.rs │ │ │ └── resolution.rs │ │ └── virtual_machine.rs │ ├── lang │ │ ├── type_.rs │ │ ├── value.rs │ │ ├── origin.rs │ │ ├── identifier.rs │ │ ├── special.rs │ │ ├── function.rs │ │ ├── operations.rs │ │ └── module.rs │ ├── api.rs │ ├── builtin │ │ └── burn │ │ │ ├── mod.rs │ │ │ ├── errors.rs │ │ │ └── types.rs │ ├── mem │ │ ├── raw.rs │ │ ├── rc.rs │ │ └── gc.rs │ ├── util │ │ └── source.rs │ ├── lib.rs │ └── parse │ │ ├── literal.rs │ │ ├── token.rs │ │ ├── node.rs │ │ └── lexer.rs ├── doc │ ├── burn.sass │ └── reference.md └── bin │ └── burn.rs ├── LICENSE.txt ├── .travis.yml ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /modules/example/src/example.burn: -------------------------------------------------------------------------------- 1 | print "Example module!" 2 | -------------------------------------------------------------------------------- /src/system_tests/statements/print.burntest: -------------------------------------------------------------------------------- 1 | print 3 2 | print 5 3 | /* OUTPUTS 4 | 3 5 | 5 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (c) 2014 Alex Deleyn 2 | 3 | No open-source license is available at this time. 4 | -------------------------------------------------------------------------------- /src/system_tests/modules/module.burntest: -------------------------------------------------------------------------------- 1 | use example 2 | print example 3 | /* OUTPUTS 4 | Example module! 5 | 6 | -------------------------------------------------------------------------------- /src/system_tests/literals/string.burntest: -------------------------------------------------------------------------------- 1 | print "foo" 2 | print "he said \"hi!\"" 3 | /* OUTPUTS 4 | foo 5 | he said "hi!" 6 | -------------------------------------------------------------------------------- /src/system_tests/statements/else.burntest: -------------------------------------------------------------------------------- 1 | if false { 2 | print "a" 3 | } else { 4 | print "b" 5 | } 6 | /* OUTPUTS 7 | b 8 | -------------------------------------------------------------------------------- /src/system_tests/literals/float.burntest: -------------------------------------------------------------------------------- 1 | print 0.0 2 | print 3.1 3 | print 0.0 is Float 4 | /* OUTPUTS 5 | 0 6 | 3.1 7 | true 8 | -------------------------------------------------------------------------------- /src/system_tests/variables/variable.burntest: -------------------------------------------------------------------------------- 1 | let $foo = 5 2 | print $foo 3 | $foo = 6 4 | print $foo 5 | /* OUTPUTS 6 | 5 7 | 6 8 | -------------------------------------------------------------------------------- /src/system_tests/statements/if.burntest: -------------------------------------------------------------------------------- 1 | if true { 2 | print "foo" 3 | } 4 | if false { 5 | print "bar" 6 | } 7 | /* OUTPUTS 8 | foo 9 | -------------------------------------------------------------------------------- /src/system_tests/expressions/call.burntest: -------------------------------------------------------------------------------- 1 | let $foo = function() { 2 | print "foo" 3 | } 4 | $foo() 5 | $foo() 6 | /* OUTPUTS 7 | foo 8 | foo 9 | -------------------------------------------------------------------------------- /src/system_tests/types/union.burntest: -------------------------------------------------------------------------------- 1 | let $Real = Integer | Float 2 | print 0 is $Real 3 | print 0.0 is $Real 4 | /* OUTPUTS 5 | true 6 | true 7 | -------------------------------------------------------------------------------- /src/system_tests/variables/duplicate_declaration.burntest: -------------------------------------------------------------------------------- 1 | let $foo 2 | let $foo 3 | /* OUTPUTS 4 | Duplicate declaration of $foo 5 | in on line 2 6 | -------------------------------------------------------------------------------- /src/system_tests/variables/use_before_declaration.burntest: -------------------------------------------------------------------------------- 1 | print $foo 2 | let $foo 3 | /* OUTPUTS 4 | Variable not found: $foo 5 | in on line 1 6 | -------------------------------------------------------------------------------- /src/system_tests/statements/while.burntest: -------------------------------------------------------------------------------- 1 | let $i = 3 2 | while $i { 3 | print "foo" 4 | $i = $i - 1 5 | } 6 | /* OUTPUTS 7 | foo 8 | foo 9 | foo 10 | -------------------------------------------------------------------------------- /src/system_tests/expressions/call_with_arguments.burntest: -------------------------------------------------------------------------------- 1 | let $foo = function( $a ) { 2 | print $a 3 | } 4 | $foo( 1 ) 5 | $foo( 2 ) 6 | /* OUTPUTS 7 | 1 8 | 2 9 | -------------------------------------------------------------------------------- /src/system_tests/scoping/bind.burntest: -------------------------------------------------------------------------------- 1 | let $foo = 3 2 | let $bar = function() { 3 | print $foo 4 | } 5 | $bar() 6 | $foo = 4 7 | $bar() 8 | /* OUTPUTS 9 | 3 10 | 4 11 | -------------------------------------------------------------------------------- /src/system_tests/statements/else_if.burntest: -------------------------------------------------------------------------------- 1 | if false { 2 | print "a" 3 | } else if true { 4 | print "b" 5 | } else { 6 | print "c" 7 | } 8 | /* OUTPUTS 9 | b 10 | -------------------------------------------------------------------------------- /modules/example/burn_module.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Dummy module for testing purposes", 3 | "version": "0.0", 4 | 5 | "sources": [ 6 | "src/example.burn" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/system_tests/scoping/block.burntest: -------------------------------------------------------------------------------- 1 | if true { 2 | let $foo = 3 3 | print $foo 4 | } 5 | if true { 6 | let $foo 7 | print $foo 8 | } 9 | /* OUTPUTS 10 | 3 11 | nothing 12 | -------------------------------------------------------------------------------- /src/system_tests/throwing/finally.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | 3 + "3" 3 | } finally { 4 | print "foo" 5 | } 6 | /* OUTPUTS 7 | foo 8 | Uncaught throwable: 9 | TypeError: Can't add and 10 | -------------------------------------------------------------------------------- /src/system_tests/scoping/nested_bind.burntest: -------------------------------------------------------------------------------- 1 | let $foo = 3 2 | let $bar = function() { 3 | function() { 4 | print $foo 5 | }() 6 | } 7 | $bar() 8 | $foo = 4 9 | $bar() 10 | /* OUTPUTS 11 | 3 12 | 4 13 | -------------------------------------------------------------------------------- /src/system_tests/throwing/throwing_finally.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | // 3 | } finally { 4 | 3 + "3" 5 | } 6 | print "foo" 7 | /* OUTPUTS 8 | Uncaught throwable: 9 | TypeError: Can't add and 10 | -------------------------------------------------------------------------------- /src/system_tests/literals/integer.burntest: -------------------------------------------------------------------------------- 1 | print 0 2 | print 3 3 | print 123 4 | print 0 is Integer 5 | print 3 is Integer 6 | print 123 is Integer 7 | /* OUTPUTS 8 | 0 9 | 3 10 | 123 11 | true 12 | true 13 | true 14 | -------------------------------------------------------------------------------- /src/system_tests/scoping/bound_assignment.burntest: -------------------------------------------------------------------------------- 1 | let $foo = 3 2 | let $bar = function() { 3 | $foo = $foo + 1 4 | } 5 | print $foo 6 | $bar() 7 | print $foo 8 | $bar() 9 | print $foo 10 | /* OUTPUTS 11 | 3 12 | 4 13 | 5 14 | -------------------------------------------------------------------------------- /src/system_tests/expressions/add.burntest: -------------------------------------------------------------------------------- 1 | print 3 + 3 2 | print 3 + 3 is Integer 3 | print 3.3 + 3.3 4 | print 3 + 3.3 5 | print 3.3 + 3 6 | print 3.1 + 1.9 is Float 7 | /* OUTPUTS 8 | 6 9 | true 10 | 6.6 11 | 6.3 12 | 6.3 13 | true 14 | -------------------------------------------------------------------------------- /src/system_tests/throwing/bad_catch_type.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | 3 + "3" 3 | } catch "not-a-type" $e { 4 | } finally { 5 | print "finally" 6 | } 7 | /* OUTPUTS 8 | finally 9 | Uncaught throwable: 10 | TypeError: is not a type 11 | -------------------------------------------------------------------------------- /src/system_tests/expressions/subtract.burntest: -------------------------------------------------------------------------------- 1 | print 3 - 3 2 | print 3 - 3 is Integer 3 | print 3.3 - 3.3 4 | print 3 - 3.3 5 | print 3.3 - 3 6 | print 3.1 - 1.1 is Float 7 | /* OUTPUTS 8 | 0 9 | true 10 | 0 11 | -0.3 12 | 0.3 13 | true 14 | -------------------------------------------------------------------------------- /src/system_tests/throwing/try_in_finally.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | 3 + "3" 3 | } finally { 4 | try { 5 | 3 + "3" 6 | } catch $e { 7 | print "caught" 8 | } 9 | } 10 | /* OUTPUTS 11 | caught 12 | Uncaught throwable: 13 | TypeError: Can't add and 14 | -------------------------------------------------------------------------------- /src/system_tests/throwing/nested_finally.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | try { 3 | 3 + "3" 4 | } finally { 5 | print "foo" 6 | } 7 | } finally { 8 | print "bar" 9 | } 10 | /* OUTPUTS 11 | foo 12 | bar 13 | Uncaught throwable: 14 | TypeError: Can't add and 15 | -------------------------------------------------------------------------------- /src/system_tests/scoping/loop_bind_static.burntest: -------------------------------------------------------------------------------- 1 | let $a 2 | let $b 3 | 4 | let $i = 2 5 | while $i { 6 | $i = $i - 1 7 | 8 | let $f = function() { 9 | print $i 10 | } 11 | 12 | if $a { 13 | $b = $f 14 | } else { 15 | $a = $f 16 | } 17 | } 18 | 19 | $a() 20 | $b() 21 | /* OUTPUTS 22 | 0 23 | 0 24 | -------------------------------------------------------------------------------- /src/system_tests/throwing/catch.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | 3 + "3" 3 | } catch $e { 4 | print "caught?" 5 | } 6 | 7 | print "caught!" 8 | 9 | try { 10 | 3 + "3" 11 | } catch ArgumentError $e { 12 | print "no" 13 | } catch TypeError $e { 14 | print "yes" 15 | } 16 | /* OUTPUTS 17 | caught? 18 | caught! 19 | yes 20 | -------------------------------------------------------------------------------- /src/system_tests/implicit/types.burntest: -------------------------------------------------------------------------------- 1 | print true is Boolean 2 | print 3 is Integer 3 | print 3.2 is Float 4 | print 3 is Number 5 | print 3.2 is Number 6 | print "foo" is String 7 | print Integer is Type 8 | print Type is Type 9 | /* OUTPUTS 10 | true 11 | true 12 | true 13 | true 14 | true 15 | true 16 | true 17 | true 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - curl -O http://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz 5 | - tar -zxf rust-nightly-x86_64-unknown-linux-gnu.tar.gz 6 | - (cd rust-nightly-x86_64-unknown-linux-gnu/ && sudo ./install.sh) 7 | 8 | script: 9 | - make burn 10 | - make tests 11 | -------------------------------------------------------------------------------- /src/libburn/vm/run/rust.rs: -------------------------------------------------------------------------------- 1 | use lang::value::Value; 2 | use vm::run::frame::Frame; 3 | use vm::virtual_machine::VirtualMachine; 4 | 5 | pub trait Operation { 6 | fn run( &mut self, &mut VirtualMachine, ::std::result::Result ) -> Result; 7 | } 8 | 9 | pub enum Result { 10 | Ok( Value ), 11 | Throw( Value ), 12 | TailBurn( Frame ), 13 | TailRust( Box ), 14 | TailYield, 15 | Burn( Frame ), 16 | Rust( Box ), 17 | Yield, 18 | } 19 | -------------------------------------------------------------------------------- /src/system_tests/throwing/else.burntest: -------------------------------------------------------------------------------- 1 | try { 2 | print "foo" 3 | } else { 4 | print "bar" 5 | } finally { 6 | print "baz" 7 | } 8 | 9 | try { 10 | print "ok" 11 | } catch $e { 12 | print "no" 13 | } else { 14 | print "ok" 15 | } 16 | 17 | try { 18 | print "foo" 19 | 3 + "3" 20 | } else { 21 | print "bar" 22 | } finally { 23 | print "baz" 24 | } 25 | /* OUTPUTS 26 | foo 27 | bar 28 | baz 29 | ok 30 | ok 31 | foo 32 | baz 33 | Uncaught throwable: 34 | TypeError: Can't add and 35 | -------------------------------------------------------------------------------- /src/libburn/lang/type_.rs: -------------------------------------------------------------------------------- 1 | use lang::value; 2 | use mem::rc::RefCounted; 3 | 4 | pub struct TypeUnion { 5 | pub left: value::Value, 6 | pub right: value::Value, 7 | } 8 | 9 | impl TypeUnion { 10 | 11 | pub fn new( left: value::Value, right: value::Value ) -> TypeUnion { 12 | TypeUnion { 13 | left: left, 14 | right: right, 15 | } 16 | } 17 | } 18 | 19 | impl RefCounted for TypeUnion {} 20 | 21 | pub struct TypeIntersection { 22 | pub left: value::Value, 23 | pub right: value::Value, 24 | } 25 | 26 | impl RefCounted for TypeIntersection {} 27 | -------------------------------------------------------------------------------- /src/libburn/vm/repl.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use mem::rc::Rc; 3 | use lang::value; 4 | use lang::identifier::Identifier; 5 | 6 | /// Persists declared variables and their values for a read-eval-print loop. 7 | pub struct State { 8 | #[doc(hidden)] 9 | pub variables: HashMap>, 10 | } 11 | 12 | impl State { 13 | 14 | /// Create a new, empty `State` instance. 15 | pub fn new() -> State { 16 | State { 17 | variables: HashMap::new(), 18 | } 19 | } 20 | 21 | #[doc(hidden)] 22 | pub fn declare_variable( &mut self, name: Identifier ) { 23 | self.variables.insert( name, Rc::new( value::Nothing ) ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/libburn/api.rs: -------------------------------------------------------------------------------- 1 | pub mod lang { 2 | pub use lang::origin; 3 | pub use lang::identifier::Identifier; 4 | pub use lang::function::Function; 5 | pub use lang::module::Module; 6 | pub use lang::special::{Special, RefCountedSpecial, StaticSpecialDef, StaticSpecial}; 7 | pub use lang::value::Value; 8 | } 9 | 10 | pub mod mem { 11 | pub use mem::rc::{Rc, RefCounted}; 12 | pub use mem::gc::{Gc, GarbageCollected}; 13 | } 14 | 15 | pub mod vm { 16 | pub use vm::virtual_machine::VirtualMachine; 17 | pub use vm::error::{Error, UncaughtThrowableHandler}; 18 | } 19 | 20 | pub mod repl { 21 | pub use vm::repl::State; 22 | } 23 | 24 | pub mod util { 25 | pub use util::source; 26 | } 27 | -------------------------------------------------------------------------------- /src/system_tests/scoping/loop_bind.burntest: -------------------------------------------------------------------------------- 1 | let $a 2 | let $b 3 | let $c 4 | let $d 5 | 6 | let $i = 4 7 | while $i { 8 | print $i 9 | let $block_i = $i 10 | 11 | let $f = function() { 12 | print "-" 13 | print $i 14 | print $block_i 15 | } 16 | 17 | // todo! use a list or something 18 | if $a { 19 | if $b { 20 | if $c { 21 | $d = $f 22 | } else { 23 | $c = $f 24 | } 25 | } else { 26 | $b = $f 27 | } 28 | } else { 29 | $a = $f 30 | } 31 | 32 | $i = $i - 1 33 | } 34 | 35 | $a() 36 | $b() 37 | $c() 38 | $d() 39 | /* OUTPUTS 40 | 4 41 | 3 42 | 2 43 | 1 44 | - 45 | 0 46 | 4 47 | - 48 | 0 49 | 3 50 | - 51 | 0 52 | 2 53 | - 54 | 0 55 | 1 56 | -------------------------------------------------------------------------------- /src/libburn/vm/run/flow.rs: -------------------------------------------------------------------------------- 1 | use lang::value::Value; 2 | 3 | #[deriving(Clone)] 4 | pub enum Flow { 5 | Running, 6 | Catching( Value ), 7 | Throwing( Value ), 8 | Returning( Value ), 9 | Jumping { pub n_flow_points: uint, pub instruction: uint }, 10 | } 11 | 12 | impl Flow { 13 | 14 | pub fn unwrap_throwable( self ) -> Value { 15 | match self { 16 | Catching( v ) | Throwing( v ) => v, 17 | _ => { unreachable!(); } 18 | } 19 | } 20 | } 21 | 22 | pub enum FlowPoint { 23 | StartCatch { pub instruction: uint }, 24 | StartFinally { pub instruction: uint }, 25 | PopFrame { pub data_stack_len: uint }, 26 | PopFrameAndRestoreFlow { pub data_stack_len: uint }, 27 | PopSuppressedFlow, 28 | } 29 | -------------------------------------------------------------------------------- /src/libburn/lang/value.rs: -------------------------------------------------------------------------------- 1 | use lang::function::Function; 2 | use mem::gc::Gc; 3 | use mem::rc::{Rc, RefCounted}; 4 | use mem::raw::Raw; 5 | use lang::type_::{TypeUnion, TypeIntersection}; 6 | use lang::module::Module; 7 | use lang::special::{StaticSpecial, RcSpecial}; 8 | 9 | #[deriving(Clone)] 10 | pub enum Value { 11 | 12 | #[doc(hidden)] 13 | Nothing, 14 | #[doc(hidden)] 15 | Boolean( bool ), 16 | #[doc(hidden)] 17 | Integer( i64 ), 18 | #[doc(hidden)] 19 | Float( f64 ), 20 | #[doc(hidden)] 21 | String( Rc ), 22 | 23 | #[doc(hidden)] 24 | Function( Gc ), 25 | #[doc(hidden)] 26 | TypeUnion( Rc ), 27 | #[doc(hidden)] 28 | TypeIntersection( Rc ), 29 | #[doc(hidden)] 30 | Module( Raw ), 31 | 32 | #[doc(hidden)] 33 | StaticSpecial( StaticSpecial ), 34 | #[doc(hidden)] 35 | RcSpecial( Rc ), 36 | } 37 | 38 | impl RefCounted for Value {} 39 | -------------------------------------------------------------------------------- /src/libburn/builtin/burn/mod.rs: -------------------------------------------------------------------------------- 1 | use lang::module::Module; 2 | 3 | pub mod errors; 4 | pub mod types; 5 | 6 | pub fn create_module() -> Module { 7 | let mut burn = Module::new(); 8 | 9 | let types = box types::create_module(); 10 | let errors = box errors::create_module(); 11 | 12 | let mut implicit = box Module::new(); 13 | implicit.add( "Boolean", types.get( "Boolean" ) ); 14 | implicit.add( "Integer", types.get( "Integer" ) ); 15 | implicit.add( "Float", types.get( "Float" ) ); 16 | implicit.add( "Number", types.get( "Number" ) ); 17 | implicit.add( "String", types.get( "String" ) ); 18 | implicit.add( "Type", types.get( "Type" ) ); 19 | implicit.add( "ArgumentError", errors.get( "ArgumentError" ) ); 20 | implicit.add( "TypeError", errors.get( "TypeError" ) ); 21 | implicit.lock(); 22 | 23 | burn.add_module( "types", types ); 24 | burn.add_module( "errors", errors ); 25 | burn.add_module( "implicit", implicit ); 26 | 27 | burn.lock(); 28 | burn 29 | } 30 | -------------------------------------------------------------------------------- /src/libburn/mem/raw.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | pub struct Raw { 5 | pub ptr: *mut T, 6 | } 7 | 8 | impl Raw { 9 | 10 | pub fn new( t: &T ) -> Raw { 11 | Raw { ptr: unsafe { mem::transmute( t ) } } 12 | } 13 | 14 | pub fn null() -> Raw { 15 | Raw { ptr: ptr::mut_null() } 16 | } 17 | 18 | pub unsafe fn get_box( &self ) -> Box { 19 | mem::transmute( self.ptr ) 20 | } 21 | 22 | pub fn is_null( &self ) -> bool { 23 | self.ptr == ptr::mut_null() 24 | } 25 | } 26 | 27 | impl Deref for Raw { 28 | fn deref<'l>( &'l self ) -> &'l T { 29 | unsafe { &*self.ptr } 30 | } 31 | } 32 | 33 | impl DerefMut for Raw { 34 | fn deref_mut<'l>( &'l mut self ) -> &'l mut T { 35 | unsafe { &mut*self.ptr } 36 | } 37 | } 38 | 39 | impl Clone for Raw { 40 | fn clone( &self ) -> Raw { 41 | Raw { ptr: self.ptr } 42 | } 43 | } 44 | 45 | impl PartialEq for Raw { 46 | fn eq( &self, other: &Raw ) -> bool { 47 | self.ptr == other.ptr 48 | } 49 | } 50 | 51 | impl Eq for Raw {} 52 | -------------------------------------------------------------------------------- /src/libburn/lang/origin.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use mem::rc::RefCounted; 3 | 4 | pub trait Origin { 5 | fn get_name<'l>( &'l self ) -> &'l str; 6 | fn get_path<'l>( &'l self ) -> Option<&'l Path>; 7 | } 8 | 9 | impl RefCounted for Box {} 10 | 11 | impl fmt::Show for Box { 12 | fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { 13 | write!( f, "{}", self.get_name() ) 14 | } 15 | } 16 | 17 | pub struct Script { 18 | pub path: Path, 19 | } 20 | 21 | impl Origin for Script { 22 | 23 | fn get_name<'l>( &'l self ) -> &'l str { 24 | self.path.as_str().unwrap() 25 | } 26 | 27 | fn get_path<'l>( &'l self ) -> Option<&'l Path> { 28 | Some( &self.path ) 29 | } 30 | } 31 | 32 | pub struct Stdin; 33 | 34 | impl Origin for Stdin { 35 | 36 | fn get_name<'l>( &'l self ) -> &'l str { 37 | "" 38 | } 39 | 40 | fn get_path<'l>( &'l self ) -> Option<&'l Path> { 41 | None 42 | } 43 | } 44 | 45 | pub struct Rust { 46 | pub name: String, 47 | } 48 | 49 | impl Origin for Rust { 50 | 51 | fn get_name<'l>( &'l self ) -> &'l str { 52 | self.name.as_slice() 53 | } 54 | 55 | fn get_path<'l>( &'l self ) -> Option<&'l Path> { 56 | None 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/libburn/vm/error.rs: -------------------------------------------------------------------------------- 1 | use lang::origin::Origin; 2 | use lang::value::Value; 3 | use mem::rc::Rc; 4 | use vm::virtual_machine::VirtualMachine; 5 | 6 | pub trait Error { 7 | fn get_message<'l>( &'l self ) -> &'l str; 8 | fn get_origin<'l>( &'l self ) -> &'l Origin; 9 | fn get_source_offset( &self ) -> uint; 10 | } 11 | 12 | pub struct ParseError { 13 | pub message: String, 14 | pub origin: Rc>, 15 | pub source_offset: uint, 16 | } 17 | 18 | impl Error for ParseError { 19 | fn get_message<'l>( &'l self ) -> &'l str { self.message.as_slice() } 20 | fn get_origin<'l>( &'l self ) -> &'l Origin { let tmp: &Origin = *self.origin; tmp } 21 | fn get_source_offset( &self ) -> uint { self.source_offset } 22 | } 23 | 24 | pub struct AnalysisError { 25 | pub message: String, 26 | pub origin: Rc>, 27 | pub source_offset: uint, 28 | } 29 | 30 | impl Error for AnalysisError { 31 | fn get_message<'l>( &'l self ) -> &'l str { self.message.as_slice() } 32 | fn get_origin<'l>( &'l self ) -> &'l Origin { let tmp: &Origin = *self.origin; tmp } 33 | fn get_source_offset( &self ) -> uint { self.source_offset } 34 | } 35 | 36 | pub trait UncaughtThrowableHandler { 37 | fn handle_uncaught_throwable( &mut self, &mut VirtualMachine, Value ); 38 | } 39 | -------------------------------------------------------------------------------- /src/libburn/util/source.rs: -------------------------------------------------------------------------------- 1 | extern crate debug; 2 | 3 | pub struct Line<'src> { 4 | pub no: uint, 5 | pub offset: uint, 6 | pub start: uint, 7 | pub end: uint, 8 | } 9 | 10 | pub fn find_line<'l>( source: &'l str, offset: uint ) -> Line<'l> { 11 | 12 | let mut chars = source.char_indices().enumerate(); 13 | 14 | let mut line = Line { 15 | no: 1, 16 | offset: source.len(), 17 | start: 0, 18 | end: 0, 19 | }; 20 | 21 | for (char_i, (byte_i, c)) in chars { 22 | 23 | if char_i == offset { 24 | 25 | line.offset = byte_i; 26 | 27 | if c == '\n' { 28 | line.end = byte_i; 29 | return line; 30 | } 31 | 32 | break; 33 | } 34 | 35 | if c == '\n' { 36 | line.start = byte_i+1; 37 | line.no += 1; 38 | } 39 | } 40 | 41 | for (_, (byte_i, c)) in chars { 42 | 43 | if c == '\n' { 44 | line.end = byte_i; 45 | return line; 46 | } 47 | } 48 | 49 | line.end = source.len(); 50 | line 51 | } 52 | 53 | #[cfg(test)] 54 | mod test { 55 | 56 | #[test] 57 | fn test() { 58 | 59 | let source = "foo僯\nbar\nbaz"; 60 | let check = | i, s: &str | { 61 | let l = find_line( source, i ); 62 | let f = format!( "{}|{}", source.slice( l.start, l.offset ), source.slice( l.offset, l.end ) ); 63 | assert!( f == s.to_string() ); 64 | }; 65 | 66 | check( 0, "|foo僯" ); 67 | check( 1, "f|oo僯" ); 68 | check( 2, "fo|o僯" ); 69 | check( 3, "foo|僯" ); 70 | check( 4, "foo僯|" ); 71 | check( 5, "|bar" ); 72 | check( 6, "b|ar" ); 73 | check( 7, "ba|r" ); 74 | check( 8, "bar|" ); 75 | check( 9, "|baz" ); 76 | check( 10, "b|az" ); 77 | check( 11, "ba|z" ); 78 | check( 12, "baz|" ); 79 | check( 13, "baz|" ); 80 | check( 14, "baz|" ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/libburn/vm/run/fiber.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use lang::value; 3 | use vm::run::frame::Frame; 4 | use vm::run::flow; 5 | 6 | pub struct Fiber { 7 | pub frame_stack: Vec, 8 | pub frame: Frame, 9 | pub flow_points: Vec, 10 | pub suppressed_flows: Vec, 11 | pub flow: flow::Flow, 12 | pub data_stack: Vec, 13 | pub on_return: Option, 14 | } 15 | 16 | impl Fiber { 17 | 18 | pub fn new( frame: Frame ) -> Fiber { 19 | Fiber { 20 | frame_stack: Vec::new(), 21 | frame: frame, 22 | flow_points: Vec::new(), 23 | suppressed_flows: Vec::new(), 24 | flow: flow::Running, 25 | data_stack: Vec::new(), 26 | on_return: None, 27 | } 28 | } 29 | 30 | pub fn pop_frame( &mut self ) -> Frame { 31 | mem::replace( &mut self.frame, self.frame_stack.pop().unwrap() ) 32 | } 33 | 34 | pub fn push_frame( &mut self, frame: Frame ) { 35 | self.frame_stack.push( mem::replace( &mut self.frame, frame ) ); 36 | } 37 | 38 | pub fn set_flow( &mut self, flow: flow::Flow ) { 39 | self.flow = flow; 40 | } 41 | 42 | pub fn replace_flow( &mut self, flow: flow::Flow ) -> flow::Flow { 43 | mem::replace( &mut self.flow, flow ) 44 | } 45 | 46 | pub fn restore_flow( &mut self ) { 47 | self.flow = self.suppressed_flows.pop().unwrap(); 48 | } 49 | 50 | pub fn pop_data( &mut self ) -> value::Value { 51 | self.data_stack.pop().unwrap() 52 | } 53 | 54 | pub fn push_data( &mut self, value: value::Value ) { 55 | self.data_stack.push( value ); 56 | } 57 | 58 | pub fn end_return( self, value: value::Value ) { 59 | if self.on_return.is_some() { 60 | self.on_return.unwrap()( value ); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/system_tests/run_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | from os import path 5 | import subprocess 6 | from difflib import Differ 7 | 8 | SHOW_OUTPUT = False 9 | EXIT_STATUS = 0 10 | 11 | def run_tests( file_or_directory ): 12 | 13 | if path.isdir( file_or_directory ): 14 | 15 | for f in os.listdir( file_or_directory ): 16 | run_tests( path.join( file_or_directory, f ) ) 17 | 18 | elif path.isfile( file_or_directory ): 19 | 20 | if file_or_directory[-9:] != ".burntest": 21 | return 22 | 23 | print "%s ..." % file_or_directory, 24 | 25 | source, expected_output = open( file_or_directory ).read().split( "\n/* OUTPUTS\n", 2 ) 26 | 27 | process = subprocess.Popen( 28 | "build/bin/burn -q -", 29 | stdin = subprocess.PIPE, 30 | stdout = subprocess.PIPE, 31 | stderr = subprocess.STDOUT, 32 | shell = True 33 | ) 34 | process.stdin.write( source ) 35 | process.stdin.close() 36 | 37 | actual_output = process.stdout.read() 38 | 39 | if actual_output == expected_output: 40 | print "\033[32;1mOK\033[0m" 41 | 42 | else: 43 | global EXIT_STATUS 44 | EXIT_STATUS = 1 45 | print "\033[31;1mFAIL\033[0m" 46 | 47 | for line in Differ().compare( expected_output.splitlines(1), actual_output.splitlines(1) ): 48 | if line[0] != "?": 49 | print "\t" + line, 50 | 51 | if SHOW_OUTPUT: 52 | print actual_output, 53 | 54 | args = sys.argv[1:] 55 | 56 | if args and args[0] == "--show-output": 57 | args.pop(0) 58 | SHOW_OUTPUT = True 59 | 60 | for arg in args or os.listdir( "." ): 61 | if not path.exists( arg ): 62 | print "File or directory does not exist: %s" % arg 63 | else: 64 | run_tests( arg ) 65 | 66 | sys.exit( EXIT_STATUS ) 67 | -------------------------------------------------------------------------------- /src/libburn/lang/identifier.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::hash::sip::SipState; 3 | use std::mem; 4 | use std::fmt; 5 | use std::collections::HashMap; 6 | use mem::raw::Raw; 7 | 8 | static mut ALL: Raw>> = Raw { ptr: 0 as *mut _ }; 9 | 10 | struct IdentifierContainer { 11 | value: String, 12 | } 13 | 14 | #[deriving(Copy, PartialEq, Eq)] 15 | pub struct Identifier { 16 | ptr: Raw, 17 | } 18 | 19 | impl Identifier { 20 | 21 | pub fn find_or_create( value: &str ) -> Identifier { 22 | unsafe { 23 | 24 | if ALL.is_null() { 25 | let all = box HashMap::>::new(); 26 | ALL = Raw::new( all ); 27 | mem::forget( all ); 28 | } 29 | 30 | let container = ALL.find_or_insert_with( 31 | value.into_string(), 32 | |_| { box IdentifierContainer { value: value.into_string() } } 33 | ); 34 | 35 | Identifier { ptr: Raw::new( *container ) } 36 | } 37 | } 38 | 39 | pub fn get_value<'l>( &'l mut self ) -> &'l str { 40 | self.ptr.value.as_slice() 41 | } 42 | } 43 | 44 | impl Hash for Identifier { 45 | fn hash( &self, state: &mut SipState ) { 46 | self.ptr.ptr.hash( state ); 47 | } 48 | } 49 | 50 | impl Clone for Identifier { 51 | fn clone( &self ) -> Identifier { 52 | *self 53 | } 54 | } 55 | 56 | impl fmt::Show for Identifier { 57 | fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { 58 | write!( f, "{}", self.ptr.value ) 59 | } 60 | } 61 | 62 | #[cfg(test)] 63 | mod test { 64 | 65 | use lang::identifier::Identifier; 66 | 67 | #[test] 68 | fn test() { 69 | let id = Identifier::find_or_create( "test" ); 70 | let id2 = Identifier::find_or_create( "test" ); 71 | assert!( id.ptr == id2.ptr ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/libburn/vm/bytecode/code.rs: -------------------------------------------------------------------------------- 1 | use mem::rc::Rc; 2 | use lang::function::FunctionDefinition; 3 | use vm::bytecode::opcode; 4 | 5 | pub struct Code { 6 | pub n_local_variables: uint, 7 | pub n_shared_local_variables: uint, 8 | pub opcodes: Vec, 9 | pub strings: Vec>, 10 | pub functions: Vec>, 11 | } 12 | 13 | impl Code { 14 | 15 | pub fn new() -> Code { 16 | Code { 17 | n_local_variables: 0, 18 | n_shared_local_variables: 0, 19 | opcodes: Vec::new(), 20 | strings: Vec::new(), 21 | functions: Vec::new(), 22 | } 23 | } 24 | 25 | pub fn dump( &self ) { 26 | println!( "\\{" ); 27 | self.dump_indented( &mut "".to_string() ); 28 | } 29 | 30 | fn dump_indented( &self, indent: &mut String ) { 31 | println!( "{} n_local_variables: {}", indent, self.n_local_variables ); 32 | println!( "{} n_shared_local_variables: {}", indent, self.n_shared_local_variables ); 33 | println!( "{} opcodes: {}", indent, self.opcodes.len() ); 34 | for (i, &c) in self.opcodes.iter().enumerate() { 35 | println!( "{} {}: {:?}", indent, i, c ); 36 | } 37 | println!( "{} strings: {}", indent, self.strings.len() ); 38 | println!( "{} functions: {}", indent, self.functions.len() ); 39 | for (i, f) in self.functions.iter().enumerate() { 40 | println!( "{} {}: \\{", indent, i ); 41 | indent.push_str( " " ); 42 | f.code.dump_indented( indent ); 43 | let n = indent.len(); 44 | indent.truncate( n - 4 ); 45 | } 46 | println!( "{}\\}", indent ); 47 | } 48 | } 49 | 50 | #[unsafe_destructor] 51 | impl Drop for Code { 52 | fn drop( &mut self ) { 53 | for c in self.opcodes.iter() { 54 | match *c { 55 | 56 | opcode::Use { operation: operation } => { 57 | unsafe { drop( operation.get_box() ); } 58 | } 59 | 60 | _ => {} 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/libburn/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type="lib"] 2 | #![crate_id="burn#0.1"] 3 | #![feature(macro_rules, struct_variant, globs)] 4 | #![allow(unnecessary_parens)] 5 | // WIP: #![warn(missing_doc)] 6 | 7 | // Can't work around this until there is a solution for 8 | // http://www.reddit.com/r/rust/comments/27fvkp/q_dont_expose_field_but_allow_incrate_usage/ 9 | #![allow(visible_private_types)] 10 | 11 | extern crate core; 12 | extern crate serialize; 13 | extern crate libc; 14 | extern crate rustuv; 15 | extern crate debug; 16 | #[cfg(test)] 17 | extern crate test; 18 | 19 | pub use api::*; 20 | mod api; 21 | 22 | macro_rules! debug ( 23 | ( $b:stmt ) => { if unsafe { ::DEBUG } { $b } } 24 | ) 25 | 26 | macro_rules! match_enum ( 27 | ( $e:expr to $p:pat => $b:block ) => { 28 | match $e { 29 | $p => $b, 30 | _ => { unreachable!(); } 31 | }; 32 | } 33 | ) 34 | 35 | mod parse { 36 | 37 | pub mod token; 38 | pub mod node; 39 | 40 | pub mod lexer; 41 | pub mod parser; 42 | 43 | pub mod literal; 44 | } 45 | 46 | mod lang { 47 | 48 | pub mod origin; 49 | pub mod identifier; 50 | 51 | pub mod module; 52 | pub mod function; 53 | pub mod type_; 54 | pub mod special; 55 | 56 | pub mod value; 57 | pub mod operations; 58 | } 59 | 60 | mod mem { 61 | pub mod raw; 62 | pub mod rc; 63 | pub mod gc; 64 | } 65 | 66 | mod vm { 67 | 68 | pub mod analysis { 69 | 70 | pub mod annotation; 71 | 72 | pub mod resolution; 73 | pub mod allocation; 74 | } 75 | 76 | pub mod bytecode { 77 | pub mod code; 78 | pub mod opcode; 79 | pub mod compiler; 80 | } 81 | 82 | pub mod run { 83 | pub mod fiber; 84 | pub mod flow; 85 | pub mod frame; 86 | pub mod rust; 87 | pub mod cpu; 88 | } 89 | 90 | pub mod error; 91 | pub mod repl; 92 | pub mod virtual_machine; 93 | } 94 | 95 | mod builtin { 96 | pub mod burn; 97 | } 98 | 99 | mod util { 100 | pub mod source; 101 | } 102 | 103 | pub static mut DEBUG: bool = false; 104 | -------------------------------------------------------------------------------- /src/libburn/builtin/burn/errors.rs: -------------------------------------------------------------------------------- 1 | use lang::value; 2 | use lang::module::Module; 3 | use lang::special; 4 | use lang::special::{StaticSpecialDef, StaticSpecial, Special, RefCountedSpecial}; 5 | use mem::rc::RefCounted; 6 | 7 | pub fn create_module() -> Module { 8 | let mut errors = Module::new(); 9 | errors.add( "TypeError", value::StaticSpecial( StaticSpecial::new( &TypeError ) ) ); 10 | errors.add( "ArgumentError", value::StaticSpecial( StaticSpecial::new( &ArgumentError ) ) ); 11 | errors.lock(); 12 | errors 13 | } 14 | 15 | 16 | 17 | static TypeError: StaticSpecialDef = StaticSpecialDef { 18 | repr: "TypeError", 19 | has_method: special::static_has_no_methods, 20 | type_test: is_type_error, 21 | }; 22 | 23 | fn is_type_error( value: &value::Value ) -> bool { 24 | match *value { 25 | value::RcSpecial( ref r ) => r.is::(), 26 | _ => false, 27 | } 28 | } 29 | 30 | struct TypeError { 31 | message: String, 32 | } 33 | 34 | impl Special for TypeError { 35 | fn repr( &self ) -> String { "".into_string() } 36 | fn to_string( &self ) -> String { format!( "TypeError: {}", self.message ) } 37 | fn is_throwable( &self ) -> bool { true } 38 | } 39 | 40 | impl RefCounted for TypeError {} 41 | impl RefCountedSpecial for TypeError {} 42 | 43 | pub fn create_type_error( message: String ) -> value::Value { 44 | special::create_rc_value( TypeError { message: message } ) 45 | } 46 | 47 | 48 | 49 | static ArgumentError: StaticSpecialDef = StaticSpecialDef { 50 | repr: "ArgumentError", 51 | has_method: special::static_has_no_methods, 52 | type_test: is_argument_error, 53 | }; 54 | 55 | fn is_argument_error( value: &value::Value ) -> bool { 56 | match *value { 57 | value::RcSpecial( ref r ) => r.is::(), 58 | _ => false, 59 | } 60 | } 61 | 62 | struct ArgumentError { 63 | message: String, 64 | } 65 | 66 | impl Special for ArgumentError { 67 | fn repr( &self ) -> String { "".into_string() } 68 | fn to_string( &self ) -> String { format!( "ArgumentError: {}", self.message ) } 69 | fn is_throwable( &self ) -> bool { true } 70 | } 71 | 72 | impl RefCounted for ArgumentError {} 73 | -------------------------------------------------------------------------------- /src/doc/burn.sass: -------------------------------------------------------------------------------- 1 | * 2 | margin: 0 3 | padding: 0 4 | font-size: 100% 5 | font-weight: inherit 6 | font-style: inherit 7 | font-family: inherit 8 | color: inherit 9 | text-decoration: inherit 10 | 11 | a 12 | color: #68A 13 | 14 | strong 15 | font-weight: bold 16 | 17 | em 18 | font-style: italic 19 | 20 | .header-section-number:after 21 | content: "." 22 | margin-right: 1ex 23 | 24 | body > h1 25 | margin: 64px 0 32px 26 | padding: 24px 16px 8px 27 | border-radius: 2px 28 | font-size: 240% 29 | background: #444 30 | color: white 31 | 32 | a 33 | color: inherit 34 | 35 | h2 36 | margin: 24px 0 37 | border-bottom: 1px solid black 38 | font-size: 200% 39 | 40 | a 41 | color: inherit 42 | 43 | h3 44 | margin: 24px 0 12px 45 | font-size: 140% 46 | 47 | a 48 | color: inherit 49 | 50 | h4 51 | margin: 12px 0 8px 52 | 53 | a 54 | color: inherit 55 | 56 | p 57 | margin: 8px 0 58 | 59 | ul 60 | margin: 8px 0 61 | padding-left: 24px 62 | 63 | li 64 | list-style: square 65 | 66 | ul 67 | margin: 0 68 | 69 | pre 70 | margin: 12px 0 71 | padding: 5px 8px 7px 72 | border: 1px solid #CCC 73 | border-radius: 2px 74 | background: #EEE 75 | line-height: 1.2 76 | 77 | code 78 | font-family: "Ubuntu mono", monospace 79 | 80 | .grammar 81 | background: #E0E8F0 82 | 83 | .note 84 | margin: 12px 0 85 | padding: 10px 86 | background: #FED 87 | 88 | 89 | 90 | body 91 | width: 800px 92 | margin: 0 auto 93 | padding-bottom: 200px 94 | font-family: Ubuntu 95 | font-size: 14px 96 | line-height: 1.6 97 | 98 | header h1 99 | margin: 32px 0 100 | font-size: 300% 101 | font-weight: bold 102 | 103 | #TOC 104 | > ul 105 | padding-left: 0 106 | 107 | ul 108 | margin-bottom: 4px 109 | 110 | li 111 | list-style: none 112 | 113 | .side_by_side 114 | margin: 12px 0 115 | overflow: auto 116 | 117 | > div 118 | pre:first-child 119 | margin-top: 0 120 | pre:last-child 121 | margin-bottom: 0 122 | 123 | > div:first-child 124 | float: left 125 | width: 48% 126 | 127 | > div:nth-child(2) 128 | margin-left: 52% 129 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIB_SRC = $(shell find src/libburn/. -type f -name '*.rs') 2 | LIB_RLIB = libburn-2d7926e1-0.1.rlib 3 | 4 | ## Recipes ## 5 | 6 | .PHONY: all clean burn lib tests unit_tests system_tests benchmarks docs reference api_docs 7 | 8 | all: burn docs 9 | clean: 10 | rm -rf build/ 11 | 12 | lib: build/lib/$(LIB_RLIB) 13 | burn: build/bin/burn 14 | 15 | tests: unit_tests system_tests 16 | unit_tests: build/tests/unit_tests 17 | RUST_TEST_TASKS=1 build/tests/unit_tests 18 | system_tests: burn 19 | python src/system_tests/run_tests.py 20 | benchmarks: build/tests/benchmarks 21 | 22 | docs: reference api_docs 23 | reference: build/doc/reference.html 24 | api_docs: build/doc/api/burn/index.html 25 | 26 | ## Library ## 27 | 28 | build/lib/$(LIB_RLIB): $(LIB_SRC) 29 | mkdir -p build/lib 30 | rustc --out-dir build/lib/ src/libburn/lib.rs 31 | 32 | ## Binaries ## 33 | 34 | build/bin/burn: build/lib/$(LIB_RLIB) src/bin/burn.rs 35 | mkdir -p build/bin 36 | rustc -L build/lib/ --out-dir build/bin src/bin/burn.rs 37 | 38 | ## Tests ## 39 | 40 | build/tests/unit_tests: $(LIB_SRC) 41 | mkdir -p build/tests 42 | rustc --test -o build/tests/unit_tests src/libburn/lib.rs 43 | 44 | build/tests/benchmarks: $(LIB_SRC) 45 | mkdir -p build/tests 46 | $(RUST_BIN)/rustc --test -o build/tests/benchmarks src/libburn/lib.rs 47 | RUST_TEST_TASKS=1 build/tests/benchmarks --bench 48 | 49 | ## Docs ## 50 | 51 | build/doc/reference.html: src/doc/reference.md src/doc/burn.sass 52 | mkdir -p build/doc 53 | sass --no-cache src/doc/burn.sass build/doc/burn.css 54 | pandoc --from=markdown --to=html5 --standalone --toc --number-sections --css burn.css -o build/doc/reference.html src/doc/reference.md 55 | 56 | build/doc/api/burn/index.html: $(LIB_SRC) 57 | mkdir -p build/doc/api 58 | rustdoc -o build/doc/api/ src/libburn/lib.rs 59 | 60 | ## Misc ## 61 | 62 | .PHONY: todo 63 | todo: 64 | grep -HrnIi --color=always -C1 "todo!" src | sed "s/^/ /" 65 | grep -HrnIi --color=always "refactor!" src | sed "s/^/ /" 66 | grep -HrnIi --color=always "optimize!" src | sed "s/^/ /" 67 | grep -HrnIi --color=always "unimplemented!" src | sed "s/^/ /" 68 | grep -HrnIi --color=always ".\{101,\}" src/libburn/ | sed "s/^/ /" 69 | -------------------------------------------------------------------------------- /src/libburn/vm/bytecode/opcode.rs: -------------------------------------------------------------------------------- 1 | use mem::raw::Raw; 2 | use lang::identifier::Identifier; 3 | 4 | pub enum OpCode { 5 | 6 | // Temp 7 | Print, 8 | ToString, 9 | 10 | // VM commands 11 | Nop, 12 | Fail, 13 | 14 | // Scopes, locals, cells 15 | // PushLocal { pub depth: u32, pub index: u32 }, 16 | // PopLocal { pub depth: u32, pub index: u32 }, 17 | 18 | // Flow 19 | PopFlowPoint, 20 | Jump { pub instruction: uint }, 21 | JumpIfPopFalsy { pub instruction: uint }, 22 | FlowJump { pub n_flow_points: uint, pub instruction: uint }, 23 | 24 | // Function flow 25 | Call { pub n_arguments: uint }, 26 | TypeCheckLocal { pub index: uint }, 27 | TypeCheckSharedLocal { pub index: uint }, 28 | Return, 29 | ReturnNothing, 30 | 31 | // Try catch 32 | PushStartCatchFlowPoint { pub instruction: uint }, 33 | PushStartFinallyFlowPoint { pub instruction: uint }, 34 | Throw, 35 | ThrownIs, 36 | CatchLocalOrJump { pub storage_index: uint, pub instruction: uint }, 37 | CatchSharedLocalOrJump { pub storage_index: uint, pub instruction: uint }, 38 | CatchLocal { pub storage_index: uint }, 39 | CatchSharedLocal { pub storage_index: uint }, 40 | Rethrow, 41 | StartFinally, 42 | EndFinally, 43 | 44 | // Data stack operations 45 | Pop, 46 | 47 | // Values 48 | PushFunction { pub index: uint }, 49 | PushString { pub index: uint }, 50 | PushFloat { pub value: f64 }, 51 | PushInteger { pub value: i64 }, 52 | PushBoolean { pub value: bool }, 53 | PushNothing, 54 | InlinedModule { pub ptr: Raw<::lang::module::Module> }, 55 | 56 | // Variables 57 | StoreLocal( uint ), 58 | LoadLocal( uint ), 59 | InitializeSharedLocal( uint ), 60 | StoreSharedLocal( uint ), 61 | LoadSharedLocal( uint ), 62 | StoreStaticBound( uint ), 63 | LoadStaticBound( uint ), 64 | StoreSharedBound( uint ), 65 | LoadSharedBound( uint ), 66 | 67 | // Names 68 | Use { pub operation: Raw<::lang::module::Use> }, 69 | LoadImplicit { pub name: Identifier }, 70 | 71 | // Access 72 | GetProperty { pub name: Identifier }, 73 | SetProperty { pub name: Identifier }, 74 | GetItem, 75 | 76 | // Operations 77 | Is, 78 | Eq, 79 | Neq, 80 | Lt, 81 | Gt, 82 | LtEq, 83 | GtEq, 84 | Union, 85 | Add, 86 | Subtract, 87 | Multiply, 88 | Divide, 89 | Not, 90 | ShortCircuitAnd, 91 | ShortCircuitOr, 92 | } 93 | -------------------------------------------------------------------------------- /src/libburn/lang/special.rs: -------------------------------------------------------------------------------- 1 | use lang::value; 2 | use lang::identifier::Identifier; 3 | use mem::rc::{Rc, RefCounted}; 4 | 5 | // todo! rust results 6 | 7 | pub trait Special { 8 | fn repr( &self ) -> String; 9 | fn to_string( &self ) -> String { self.repr() } 10 | fn is_truthy( &self ) -> bool { true } 11 | fn is_type( &self ) -> bool { false } 12 | fn type_test( &self, &value::Value ) -> bool { unreachable!() } 13 | fn is_throwable( &self ) -> bool { false } 14 | } 15 | 16 | pub trait RefCountedSpecial : Special + RefCounted {} 17 | 18 | pub struct RcSpecial { 19 | type_id: ::core::intrinsics::TypeId, 20 | special: Box, 21 | } 22 | 23 | impl RcSpecial { 24 | 25 | pub fn is( &self ) -> bool { 26 | unsafe { ::core::intrinsics::type_id::() == self.type_id } 27 | } 28 | } 29 | 30 | impl Deref> for RcSpecial { 31 | fn deref<'l>( &'l self ) -> &'l Box { 32 | & self.special 33 | } 34 | } 35 | 36 | impl DerefMut> for RcSpecial { 37 | fn deref_mut<'l>( &'l mut self ) -> &'l mut Box { 38 | &mut self.special 39 | } 40 | } 41 | 42 | impl RefCounted for RcSpecial {} 43 | 44 | pub fn create_rc_value( special: T ) -> value::Value { 45 | value::RcSpecial( Rc::new( RcSpecial { 46 | type_id: unsafe { ::core::intrinsics::type_id::() }, 47 | special: box special, 48 | } ) ) 49 | } 50 | 51 | 52 | 53 | pub fn static_has_no_methods( _: Identifier ) -> bool { false } 54 | pub fn static_not_a_type( _: &value::Value ) -> bool { unreachable!() } 55 | 56 | pub struct StaticSpecialDef { 57 | pub repr: &'static str, 58 | pub has_method: fn ( Identifier ) -> bool, 59 | pub type_test: fn ( &value::Value ) -> bool, 60 | } 61 | 62 | #[deriving(Clone)] 63 | pub struct StaticSpecial { 64 | def: &'static StaticSpecialDef, 65 | } 66 | 67 | impl StaticSpecial { 68 | 69 | pub fn new( def: &'static StaticSpecialDef ) -> StaticSpecial { 70 | StaticSpecial { def: def } 71 | } 72 | 73 | pub fn repr( self ) -> String { self.def.repr.to_string() } 74 | pub fn is_truthy( self ) -> bool { true } 75 | pub fn is_type( self ) -> bool { &self.def.type_test as *_ != &static_not_a_type as *_ } 76 | pub fn type_test( self, value: &value::Value ) -> bool { ( self.def.type_test )( value ) } 77 | pub fn is_throwable( self ) -> bool { false } 78 | } 79 | -------------------------------------------------------------------------------- /src/libburn/parse/literal.rs: -------------------------------------------------------------------------------- 1 | use std::str::utf8_char_width; 2 | 3 | pub fn parse_int( source: &str ) -> Result { 4 | match from_str::( source ) { 5 | Some( i ) => Ok( i ), 6 | None => Err( "Integer literal is out of range.".to_string() ), 7 | } 8 | } 9 | 10 | pub fn parse_float( source: &str ) -> Result { 11 | match from_str::( source ) { 12 | Some( f ) => Ok( f ), 13 | None => Err( "Float literal is out of range.".to_string() ), 14 | } 15 | } 16 | 17 | pub fn parse_string( source: &str ) -> Result { 18 | let mut buf = String::new(); 19 | let raw: bool; 20 | let delimiter: char; 21 | let mut i: uint; 22 | 23 | if source[0] as char == 'r' { 24 | raw = true; 25 | delimiter = source[1] as char; 26 | i = 2; 27 | } else { 28 | raw = false; 29 | delimiter = source[0] as char; 30 | i = 1; 31 | } 32 | 33 | loop { 34 | match source[i] as char { 35 | '\\' => { 36 | match source[i+1] as char { 37 | '\\' => { 38 | buf.push_char( '\\' ); 39 | i += 2; 40 | } 41 | c @ _ if c == delimiter => { 42 | buf.push_char( delimiter ); 43 | i += 2; 44 | } 45 | _ if raw => { 46 | buf.push_char( '\\' ); 47 | i += 1; 48 | } 49 | 'n' => { 50 | buf.push_char( '\n' ); 51 | i += 2; 52 | } 53 | 't' => { 54 | buf.push_char( '\t' ); 55 | i += 2; 56 | } 57 | _ => { 58 | return Err( ("Invalid escape sequence".to_string(), i) ); 59 | } 60 | } 61 | }, 62 | c @ _ if c == delimiter => break, 63 | _ => { 64 | buf.push_char( source.char_at( i ) ); 65 | i += utf8_char_width( source[ i ] ); 66 | } 67 | } 68 | } 69 | 70 | Ok( buf.into_string() ) 71 | } 72 | 73 | #[cfg(test)] 74 | mod test { 75 | 76 | use super::{parse_int, parse_float, parse_string}; 77 | 78 | #[test] 79 | fn test_parse_int() { 80 | assert!( parse_int( "0" ) == Ok( 0 ) ); 81 | assert!( parse_int( "3" ) == Ok( 3 ) ); 82 | assert!( parse_int( "123456789" ) == Ok( 123456789 ) ); 83 | assert!( parse_int( "-10" ) == Ok( -10 ) ); 84 | assert!( parse_int( "99999999999999999999999999999999999" ) 85 | == Err( "Integer literal is out of range.".to_string() ) ); 86 | } 87 | 88 | #[test] 89 | fn test_parse_float() { 90 | assert!( parse_float( "3.1" ) == Ok( 3.1 ) ); 91 | } 92 | 93 | #[test] 94 | fn test_parse_string() { 95 | assert!( parse_string( r#""test""# ) == Ok( "test".to_string() ) ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **UPDATE:** Work on burn is ongoing at [rainbow-alex/burn.js](http://www.github.com/rainbow-alex/burn.js). 2 | 3 | Burn intends to be a light-weight, general-purpose programming language. 4 | Here are some of its properties you're likely to know from existing languages: 5 | 6 | * Newlines separate statements. Curly braces are mandatory. 7 | * Block scoping. 8 | * Values have types, variables don't. Values are not implicitely coerced. 9 | * Functions are first-class values and can have free variables (lexical closure). 10 | * Object-oriented programming with multiple inheritance. Classes are first-class values too, and support lexical closure. 11 | * Memory is garbage-collected. 12 | * Concurrency is achieved through fibers. 13 | * It compiles to bytecode at runtime. This bytecode is (to some small degree) self-optimizing. 14 | 15 | 16 | 17 | Of course there's more to Burn than that. Here are some of the twists that really set Burn apart: 18 | 19 | 20 | 21 | **Typing** 22 | 23 | Burn's types are more like mathematical sets than the type systems you are probably familiar with. 24 | Think Venn diagrams rather than family trees. 25 | It has the usual types like `Integer` or `String`, but also `Positive` or `Empty`. 26 | 27 | There are a lot of types in the standard library, but you can create your own as well. 28 | Types are first-class values and can be combined and created ad hoc: 29 | 30 | ``` 31 | let $Real = Integer | Float 32 | let $NonEmptyString = String + not Empty 33 | let $HttpUrl = String.starting_with( "http://" ) 34 | let $CustomType = function( $value ) { return } 35 | ``` 36 | 37 | 38 | 39 | **Object capabilities** 40 | 41 | Burn follows an object-capability model. 42 | Capabilities are functions or objects that can do things with side-effects (e.g. read a file or print to stdout). 43 | All capabilities are given to the program entry point (i.e. `main`), and it is then responsible for passing them to other parts of the program. 44 | This is basically nothing more than language-enforced dependency injection. 45 | 46 | * You can, at any time, safely import a library or load a script. It can only have side-effects if and when you give it the required capabilities. 47 | * An untrusted Burn program can be run with reduced capabilities, or by lazily prompting them from the user. 48 | * You can safely embed Burn without cutting off access to third party libraries by simply giving it reduced capabilities. 49 | * Burn programs are probably more likely to be reusable and testable. 50 | -------------------------------------------------------------------------------- /src/libburn/lang/function.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | use mem::rc::{Rc, RefCounted}; 3 | use mem::gc::GarbageCollected; 4 | use lang::value; 5 | use lang::identifier::Identifier; 6 | use vm::bytecode::code::Code; 7 | 8 | pub struct Function { 9 | #[doc(hidden)] 10 | pub definition: Rc, 11 | #[doc(hidden)] 12 | pub static_bound_variables: Vec, 13 | #[doc(hidden)] 14 | pub shared_bound_variables: Vec>, 15 | } 16 | 17 | impl Function { 18 | 19 | pub fn new( 20 | mut definition: Rc 21 | ) -> Function { 22 | 23 | let n_bindings = definition.bindings.len(); 24 | let n_static = definition.n_static_bound_variables; 25 | let n_shared = n_bindings - n_static; 26 | 27 | Function { 28 | definition: definition, 29 | static_bound_variables: Vec::from_elem( n_static, value::Nothing ), 30 | shared_bound_variables: Vec::from_fn( n_shared, |_| { Rc::new( value::Nothing ) } ), 31 | } 32 | } 33 | } 34 | 35 | impl GarbageCollected for Function { 36 | 37 | fn mark( &mut self ) { 38 | unimplemented!(); 39 | } 40 | } 41 | 42 | pub struct FunctionDefinition { 43 | pub parameters: Vec, 44 | pub bindings: Vec, 45 | pub code: Box, 46 | pub n_static_bound_variables: uint, 47 | } 48 | 49 | impl FunctionDefinition { 50 | 51 | pub fn new( 52 | code: Box, 53 | parameters: Vec, 54 | bindings: Vec 55 | ) -> FunctionDefinition { 56 | 57 | let n_static_bound_variables = bindings.iter().filter( |b| { 58 | match **b { 59 | LocalToStaticBoundBinding(..) | StaticBoundToStaticBoundBinding(..) => true, 60 | _ => false, 61 | } 62 | } ).count(); 63 | 64 | FunctionDefinition { 65 | parameters: parameters, 66 | bindings: bindings, 67 | code: code, 68 | n_static_bound_variables: n_static_bound_variables, 69 | } 70 | } 71 | } 72 | 73 | impl RefCounted for FunctionDefinition {} 74 | 75 | pub struct FunctionParameterDefinition { 76 | pub name: Identifier, 77 | pub storage: FunctionParameterStorage, 78 | } 79 | 80 | pub enum FunctionParameterStorage { 81 | LocalFunctionParameterStorage( uint ), 82 | SharedLocalFunctionParameterStorage( uint ), 83 | } 84 | 85 | pub enum FunctionBindingDefinition { 86 | LocalToStaticBoundBinding( uint, uint ), 87 | SharedLocalToSharedBoundBinding( uint, uint ), 88 | StaticBoundToStaticBoundBinding( uint, uint ), 89 | SharedBoundToSharedBoundBinding( uint, uint ), 90 | } 91 | -------------------------------------------------------------------------------- /src/libburn/mem/rc.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | #[unsafe_no_drop_flag] 5 | pub struct Rc { 6 | ptr: *mut RcWrapper, 7 | } 8 | 9 | impl Rc { 10 | 11 | pub fn new( thing: T ) -> Rc { 12 | let mut rc_wrapper = box RcWrapper { 13 | rc: 1, 14 | value: thing, 15 | }; 16 | let ptr = &mut *rc_wrapper as *mut RcWrapper; 17 | unsafe { mem::forget( rc_wrapper ); } 18 | Rc { ptr: ptr } 19 | } 20 | } 21 | 22 | impl Deref for Rc { 23 | fn deref<'l>( &'l self ) -> &'l T { 24 | unsafe { & (*self.ptr).value } 25 | } 26 | } 27 | 28 | impl DerefMut for Rc { 29 | fn deref_mut<'l>( &'l mut self ) -> &'l mut T { 30 | unsafe { &mut (*self.ptr).value } 31 | } 32 | } 33 | 34 | impl Clone for Rc { 35 | fn clone( &self ) -> Rc { 36 | unsafe { (*self.ptr).rc += 1; } 37 | Rc { ptr: self.ptr } 38 | } 39 | } 40 | 41 | #[unsafe_destructor] 42 | impl Drop for Rc { 43 | fn drop( &mut self ) { 44 | unsafe { 45 | if ! self.ptr.is_null() { 46 | 47 | (*self.ptr).rc -= 1; 48 | if (*self.ptr).rc == 0 { 49 | drop( mem::transmute::<_,Box>>( self.ptr ) ); 50 | } 51 | 52 | self.ptr = ptr::mut_null(); 53 | } 54 | } 55 | } 56 | } 57 | 58 | struct RcWrapper { 59 | rc: uint, 60 | value: T, 61 | } 62 | 63 | pub trait RefCounted {} 64 | 65 | impl RefCounted for String {} 66 | 67 | #[cfg(test)] 68 | mod test { 69 | 70 | use mem::rc::{Rc, RefCounted}; 71 | 72 | struct Thing { 73 | dropped: *mut bool, 74 | } 75 | 76 | impl Thing { 77 | 78 | pub fn new( dropped: *mut bool ) -> Thing { 79 | Thing { dropped: dropped } 80 | } 81 | } 82 | 83 | impl RefCounted for Thing {} 84 | 85 | impl Drop for Thing { 86 | fn drop( &mut self ) { 87 | unsafe { *self.dropped = true; } 88 | } 89 | } 90 | 91 | #[test] 92 | fn test_drop() { 93 | let mut dropped = false; 94 | let thing = Thing::new( &mut dropped ); 95 | 96 | let r = Rc::new( thing ); 97 | assert!( ! dropped ); 98 | 99 | drop( r ); 100 | assert!( dropped ); 101 | } 102 | 103 | #[test] 104 | fn test_clone() { 105 | let mut dropped = false; 106 | let thing = Thing::new( &mut dropped ); 107 | 108 | let r1 = Rc::new( thing ); 109 | let r2 = r1.clone(); 110 | 111 | drop( r1 ); 112 | assert!( ! dropped ); 113 | 114 | drop( r2 ); 115 | assert!( dropped ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/libburn/vm/run/frame.rs: -------------------------------------------------------------------------------- 1 | use mem::rc::Rc; 2 | use mem::gc::Gc; 3 | use lang::value::Value; 4 | use lang::origin::Origin; 5 | use lang::function::Function; 6 | use vm::bytecode::code::Code; 7 | use vm::run::rust; 8 | 9 | pub enum Frame { 10 | 11 | BurnRootFrame { 12 | pub origin: Rc>, 13 | pub code: Box, 14 | pub context: BurnContext, 15 | }, 16 | 17 | BurnFunctionFrame { 18 | pub function: Gc, 19 | pub context: BurnContext, 20 | }, 21 | 22 | RustOperationFrame( Box ), 23 | } 24 | 25 | impl Frame { 26 | 27 | pub fn is_rust_operation( &self ) -> bool { 28 | match *self { 29 | RustOperationFrame(..) => true, 30 | _ => false, 31 | } 32 | } 33 | 34 | pub fn get_code<'l>( &'l mut self ) -> &'l mut Code { 35 | match *self { 36 | BurnRootFrame { code: ref mut code, .. } => &mut **code, 37 | BurnFunctionFrame { function: ref mut function, .. } => &mut *function.definition.code, 38 | 39 | RustOperationFrame(..) => { unreachable!(); } 40 | } 41 | } 42 | 43 | // optimize! unsafe get for performance? 44 | pub fn get_context<'l>( &'l mut self ) -> &'l mut BurnContext { 45 | match *self { 46 | BurnRootFrame { context: ref mut context,.. } 47 | | BurnFunctionFrame { context: ref mut context, .. } 48 | => context, 49 | 50 | RustOperationFrame(..) => { unreachable!(); } 51 | } 52 | } 53 | 54 | pub fn get_rust_operation<'l>( &'l mut self ) -> &'l mut Box { 55 | match *self { 56 | RustOperationFrame( ref mut operation ) => operation, 57 | _ => { unreachable!(); } 58 | } 59 | } 60 | 61 | pub fn get_local_variable<'l>( &'l mut self, index: uint ) -> &'l mut Value { 62 | self.get_context().local_variables.get_mut( index ) 63 | } 64 | 65 | pub fn get_shared_local_variable<'l>( &'l mut self, index: uint ) -> &'l mut Option> { 66 | self.get_context().shared_local_variables.get_mut( index ) 67 | } 68 | 69 | fn get_closure<'l>( &'l mut self ) -> &'l mut Function { 70 | match_enum!( *self to BurnFunctionFrame { function: ref mut function, .. } => { &mut **function } ) 71 | } 72 | 73 | pub fn get_static_bound_variable<'l>( &'l mut self, index: uint ) -> &'l mut Value { 74 | self.get_closure().static_bound_variables.get_mut( index ) 75 | } 76 | 77 | pub fn get_shared_bound_variable<'l>( &'l mut self, index: uint ) -> &'l mut Rc { 78 | self.get_closure().shared_bound_variables.get_mut( index ) 79 | } 80 | } 81 | 82 | type Locals = Vec; 83 | type SharedLocals = Vec>>; 84 | 85 | pub struct BurnContext { 86 | // optimize! the length of *_variables is known via the type 87 | // so instead of a vec, this could be a pointer to a fixed-size buffer 88 | pub local_variables: Locals, 89 | // optimize! someday, rust should be able to store Option> in one word 90 | pub shared_local_variables: SharedLocals, 91 | pub instruction: uint, 92 | } 93 | 94 | impl BurnContext { 95 | 96 | pub fn new( locals: Locals, shared: SharedLocals ) -> BurnContext { 97 | BurnContext { 98 | local_variables: locals, 99 | shared_local_variables: shared, 100 | instruction: 0, 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/libburn/builtin/burn/types.rs: -------------------------------------------------------------------------------- 1 | use lang::value; 2 | use lang::special; 3 | use lang::special::{StaticSpecialDef, StaticSpecial}; 4 | use lang::module::Module; 5 | 6 | pub fn create_module() -> Module { 7 | let mut types = Module::new(); 8 | types.add( "Boolean", value::StaticSpecial( StaticSpecial::new( &Boolean ) ) ); 9 | types.add( "Integer", value::StaticSpecial( StaticSpecial::new( &Integer ) ) ); 10 | types.add( "Float", value::StaticSpecial( StaticSpecial::new( &Float ) ) ); 11 | types.add( "Number", value::StaticSpecial( StaticSpecial::new( &Number ) ) ); 12 | types.add( "String", value::StaticSpecial( StaticSpecial::new( &String ) ) ); 13 | types.add( "Type", value::StaticSpecial( StaticSpecial::new( &Type ) ) ); 14 | types.add( "Throwable", value::StaticSpecial( StaticSpecial::new( &Throwable ) ) ); 15 | types.lock(); 16 | types 17 | } 18 | 19 | 20 | 21 | static Boolean: StaticSpecialDef = StaticSpecialDef { 22 | repr: "Boolean", 23 | has_method: special::static_has_no_methods, 24 | type_test: is_boolean, 25 | }; 26 | 27 | fn is_boolean( value: &value::Value ) -> bool { 28 | match *value { 29 | value::Boolean(..) => true, 30 | _ => false, 31 | } 32 | } 33 | 34 | 35 | 36 | static Integer: StaticSpecialDef = StaticSpecialDef { 37 | repr: "Integer", 38 | has_method: special::static_has_no_methods, 39 | type_test: is_integer, 40 | }; 41 | 42 | fn is_integer( value: &value::Value ) -> bool { 43 | match *value { 44 | value::Integer(..) => true, 45 | _ => false, 46 | } 47 | } 48 | 49 | 50 | 51 | static Float: StaticSpecialDef = StaticSpecialDef { 52 | repr: "Float", 53 | has_method: special::static_has_no_methods, 54 | type_test: is_float, 55 | }; 56 | 57 | fn is_float( value: &value::Value ) -> bool { 58 | match *value { 59 | value::Float(..) => true, 60 | _ => false, 61 | } 62 | } 63 | 64 | 65 | 66 | static Number: StaticSpecialDef = StaticSpecialDef { 67 | repr: "Number", 68 | has_method: special::static_has_no_methods, 69 | type_test: is_number, 70 | }; 71 | 72 | fn is_number( value: &value::Value ) -> bool { 73 | match *value { 74 | value::Integer(..) | value::Float(..) => true, 75 | _ => false, 76 | } 77 | } 78 | 79 | 80 | 81 | static String: StaticSpecialDef = StaticSpecialDef { 82 | repr: "String", 83 | has_method: special::static_has_no_methods, 84 | type_test: is_string, 85 | }; 86 | 87 | fn is_string( value: &value::Value ) -> bool { 88 | match *value { 89 | value::String(..) => true, 90 | _ => false, 91 | } 92 | } 93 | 94 | 95 | 96 | static Type: StaticSpecialDef = StaticSpecialDef { 97 | repr: "Type", 98 | has_method: special::static_has_no_methods, 99 | type_test: is_type, 100 | }; 101 | 102 | pub fn is_type( value: &value::Value ) -> bool { 103 | match *value { 104 | value::TypeUnion(..) => true, 105 | value::TypeIntersection(..) => true, 106 | value::StaticSpecial( special ) => special.is_type(), 107 | value::RcSpecial( ref r ) => r.is_type(), 108 | _ => false, 109 | } 110 | } 111 | 112 | 113 | 114 | static Throwable: StaticSpecialDef = StaticSpecialDef { 115 | repr: "Throwable", 116 | has_method: special::static_has_no_methods, 117 | type_test: is_throwable, 118 | }; 119 | 120 | pub fn is_throwable( value: &value::Value ) -> bool { 121 | match *value { 122 | value::RcSpecial( ref r ) => r.is_throwable(), 123 | _ => false, 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/libburn/vm/analysis/annotation.rs: -------------------------------------------------------------------------------- 1 | use mem::raw::Raw; 2 | use parse::node; 3 | use lang::identifier::Identifier; 4 | 5 | pub type Time = uint; 6 | 7 | pub struct Frame { 8 | pub declared_variables: Vec>, 9 | pub functions: Vec>, 10 | pub n_local_variables: uint, 11 | pub n_shared_local_variables: uint, 12 | pub closure: Option, 13 | } 14 | 15 | impl Frame { 16 | 17 | pub fn new() -> Frame { 18 | Frame { 19 | declared_variables: Vec::new(), 20 | functions: Vec::new(), 21 | n_local_variables: 0, 22 | n_shared_local_variables: 0, 23 | closure: None, 24 | } 25 | } 26 | 27 | pub fn new_with_closure() -> Frame { 28 | Frame { 29 | declared_variables: Vec::new(), 30 | functions: Vec::new(), 31 | n_local_variables: 0, 32 | n_shared_local_variables: 0, 33 | closure: Some( Closure::new() ), 34 | } 35 | } 36 | 37 | pub fn get_closure<'l>( &'l mut self ) -> &'l mut Closure { 38 | self.closure.as_mut().unwrap() 39 | } 40 | } 41 | 42 | impl PartialEq for Frame { 43 | fn eq( &self, other: &Frame ) -> bool { 44 | self as *Frame == other as *Frame 45 | } 46 | } 47 | 48 | impl Eq for Frame {} 49 | 50 | pub struct Variable { 51 | pub name: Identifier, 52 | pub declared_in: Raw, 53 | pub reads: Vec, 54 | pub writes: Vec, 55 | pub root_binds: Vec, 56 | pub n_binds: uint, 57 | pub local_storage_type: storage::LocalStorageType, 58 | pub local_storage_index: uint, 59 | pub bound_storage_type: storage::BoundStorageType, 60 | } 61 | 62 | impl Variable { 63 | 64 | pub fn new( name: Identifier ) -> Variable { 65 | Variable { 66 | name: name, 67 | declared_in: Raw::null(), 68 | reads: Vec::new(), 69 | writes: Vec::new(), 70 | root_binds: Vec::new(), 71 | n_binds: 0, 72 | local_storage_type: storage::SharedLocal, 73 | local_storage_index: 0, 74 | bound_storage_type: storage::SharedBound, 75 | // bound_storage_index differs per Binding 76 | } 77 | } 78 | } 79 | 80 | impl PartialEq for Variable { 81 | fn eq( &self, other: &Variable ) -> bool { 82 | self as *Variable == other as *Variable 83 | } 84 | } 85 | 86 | impl Eq for Variable {} 87 | 88 | pub struct ReadVariable { 89 | pub time: Time, 90 | } 91 | 92 | pub struct WriteVariable { 93 | pub time: Time, 94 | } 95 | 96 | pub struct BindVariable { 97 | pub time: Time, 98 | pub mutable: bool, 99 | } 100 | 101 | pub struct Closure { 102 | pub created_at: Time, 103 | pub bindings: Vec, 104 | pub n_static_bound_variables: uint, 105 | pub n_shared_bound_variables: uint, 106 | } 107 | 108 | impl Closure { 109 | 110 | pub fn new() -> Closure { 111 | Closure { 112 | created_at: 0, 113 | bindings: Vec::new(), 114 | n_static_bound_variables: 0, 115 | n_shared_bound_variables: 0, 116 | } 117 | } 118 | } 119 | 120 | pub struct Binding { 121 | pub variable: Raw, 122 | pub mutable: bool, 123 | pub storage_index: uint, 124 | } 125 | 126 | pub struct Use { 127 | pub name: Identifier, 128 | pub operation: Raw<::lang::module::Use>, 129 | } 130 | 131 | impl Use { 132 | 133 | pub fn new( name: Identifier ) -> Use { 134 | Use { 135 | name: name, 136 | operation: Raw::null(), 137 | } 138 | } 139 | } 140 | 141 | pub struct Name { 142 | pub resolution: NameResolution, 143 | } 144 | 145 | impl Name { 146 | 147 | pub fn new() -> Name { 148 | Name { 149 | resolution: Implicit, 150 | } 151 | } 152 | } 153 | 154 | pub enum NameResolution { 155 | Implicit, 156 | Use( Raw ), 157 | } 158 | 159 | pub mod storage { 160 | 161 | pub enum LocalStorageType { 162 | Local, 163 | SharedLocal, 164 | } 165 | 166 | pub enum BoundStorageType { 167 | StaticBound, 168 | SharedBound, 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/libburn/parse/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[deriving(PartialEq, Eq)] 4 | pub enum Token<'src> { 5 | Whitespace, 6 | Newline, 7 | 8 | LeftCurlyBracket, // { 9 | RightCurlyBracket, // } 10 | LeftSquareBracket, // [ 11 | RightSquareBracket, // ] 12 | LeftParenthesis, // ( 13 | RightParenthesis, // ) 14 | LeftAngleBracket, // < 15 | RightAngleBracket, // > 16 | Dot, // . 17 | Comma, // , 18 | VerticalBar, // | 19 | Equals, // = 20 | Plus, // + 21 | Dash, // - 22 | Asterisk, // * 23 | Slash, // / 24 | Percent, // % 25 | 26 | LeftAngleBracketEquals, // <= 27 | RightAngleBracketEquals, // >= 28 | PlusEquals, // += 29 | DashEquals, // -= 30 | AsteriskEquals, // *= 31 | SlashEquals, // /= 32 | PercentEquals, // %= 33 | EqualsEquals, // == 34 | BangEquals, // != 35 | 36 | Arrow, // -> 37 | 38 | And, 39 | Catch, 40 | Class, 41 | Else, 42 | False, 43 | Finally, 44 | For, 45 | Function, 46 | If, 47 | In, 48 | Is, 49 | Let, 50 | New, 51 | Not, 52 | Nothing, 53 | Or, 54 | Print, 55 | Return, 56 | This, 57 | Throw, 58 | True, 59 | Try, 60 | While, 61 | Use, 62 | 63 | Identifier( &'src str ), // e.g. foobar 64 | Variable( &'src str ), // e.g. $foobar (only foobar is stored) 65 | 66 | String( &'src str ), 67 | Integer( &'src str ), 68 | Float( &'src str ), 69 | 70 | Eof, 71 | 72 | Error( &'static str ), 73 | } 74 | 75 | impl<'src> fmt::Show for Token<'src> { 76 | fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { 77 | match *self { 78 | 79 | Whitespace => write!( f, "whitespace" ), 80 | Newline => write!( f, "newline" ), 81 | 82 | LeftCurlyBracket => write!( f, "`\\{`" ), 83 | RightCurlyBracket => write!( f, "`\\}`" ), 84 | LeftSquareBracket => write!( f, "`[`" ), 85 | RightSquareBracket => write!( f, "`]`" ), 86 | LeftParenthesis => write!( f, "`(`" ), 87 | RightParenthesis => write!( f, "`)`" ), 88 | LeftAngleBracket => write!( f, "`<`" ), 89 | RightAngleBracket => write!( f, "`>`" ), 90 | Dot => write!( f, "`.`" ), 91 | Comma => write!( f, "`,`" ), 92 | VerticalBar => write!( f, "`|`" ), 93 | Equals => write!( f, "`=`" ), 94 | Plus => write!( f, "`+`" ), 95 | Dash => write!( f, "`-`" ), 96 | Asterisk => write!( f, "`*`" ), 97 | Slash => write!( f, "`/`" ), 98 | Percent => write!( f, "`%`" ), 99 | 100 | LeftAngleBracketEquals => write!( f, "`<=`" ), 101 | RightAngleBracketEquals => write!( f, "`>=`" ), 102 | PlusEquals => write!( f, "`+=`" ), 103 | DashEquals => write!( f, "`-=`" ), 104 | AsteriskEquals => write!( f, "`*=`" ), 105 | SlashEquals => write!( f, "`/=`" ), 106 | PercentEquals => write!( f, "`%=`" ), 107 | EqualsEquals => write!( f, "`==`" ), 108 | BangEquals => write!( f, "`!=`" ), 109 | 110 | Arrow => write!( f, "`->`" ), 111 | 112 | And => write!( f, "and" ), 113 | Catch => write!( f, "catch" ), 114 | Class => write!( f, "class" ), 115 | Else => write!( f, "else" ), 116 | False => write!( f, "false" ), 117 | Finally => write!( f, "finally" ), 118 | For => write!( f, "for" ), 119 | Function => write!( f, "function" ), 120 | If => write!( f, "if" ), 121 | In => write!( f, "in" ), 122 | Is => write!( f, "is" ), 123 | Let => write!( f, "let" ), 124 | New => write!( f, "new" ), 125 | Not => write!( f, "not" ), 126 | Nothing => write!( f, "nothing" ), 127 | Or => write!( f, "or" ), 128 | Print => write!( f, "print" ), 129 | Return => write!( f, "return" ), 130 | This => write!( f, "this" ), 131 | Throw => write!( f, "throw" ), 132 | True => write!( f, "true" ), 133 | Try => write!( f, "try" ), 134 | While => write!( f, "while" ), 135 | Use => write!( f, "use" ), 136 | 137 | Identifier( v ) => write!( f, "IDENTIFIER({})", v ), 138 | Variable( v ) => write!( f, "VARIABLE(${})", v ), 139 | 140 | String( v ) => write!( f, "STRING({})", v ), 141 | Integer( v ) => write!( f, "INTEGER({})", v ), 142 | Float( v ) => write!( f, "FLOAT({})", v ), 143 | 144 | Eof => write!( f, "" ), 145 | 146 | Error( m ) => write!( f, "ERROR({})", m ), 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/libburn/mem/gc.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | #[unsafe_no_drop_flag] 5 | pub struct Gc { 6 | ptr: *mut GcWrapper, 7 | } 8 | 9 | impl Deref for Gc { 10 | fn deref<'l>( &'l self ) -> &'l T { 11 | unsafe { & (*self.ptr).value } 12 | } 13 | } 14 | 15 | impl DerefMut for Gc { 16 | fn deref_mut<'l>( &'l mut self ) -> &'l mut T { 17 | unsafe { &mut (*self.ptr).value } 18 | } 19 | } 20 | 21 | impl Clone for Gc { 22 | fn clone( &self ) -> Gc { 23 | unsafe { (*self.ptr).rc += 1; } 24 | Gc { ptr: self.ptr } 25 | } 26 | } 27 | 28 | #[unsafe_destructor] 29 | impl Drop for Gc { 30 | fn drop( &mut self ) { 31 | unsafe { 32 | if ! self.ptr.is_null() { 33 | 34 | (*self.ptr).rc -= 1; 35 | if (*self.ptr).rc == 0 { 36 | self.die(); 37 | } 38 | 39 | self.ptr = ptr::mut_null(); 40 | } 41 | } 42 | } 43 | } 44 | 45 | pub struct GcWrapper { 46 | rc: uint, 47 | marked: bool, 48 | is_immortal: bool, 49 | value: T, 50 | } 51 | 52 | pub trait GarbageCollected { 53 | 54 | fn mark( &mut self ); 55 | 56 | fn die( &mut self ) { 57 | } 58 | } 59 | 60 | pub struct GarbageCollectedManager { 61 | alive: Vec<*mut GcWrapper>, 62 | immortal: Vec>>, 63 | } 64 | 65 | impl GarbageCollectedManager { 66 | pub fn new() -> GarbageCollectedManager { 67 | GarbageCollectedManager { 68 | alive: Vec::new(), 69 | immortal: Vec::new(), 70 | } 71 | } 72 | 73 | pub fn register( &mut self, thing: T ) -> Gc { 74 | 75 | let mut gc_wrapper = box GcWrapper { 76 | rc: 1, 77 | marked: false, 78 | is_immortal: false, 79 | value: thing, 80 | }; 81 | 82 | let ptr = &mut *gc_wrapper as *mut GcWrapper; 83 | self.alive.push( ptr ); 84 | unsafe { mem::forget( gc_wrapper ); } 85 | 86 | Gc { ptr: ptr } 87 | } 88 | 89 | pub fn sweep( &mut self ) { 90 | unsafe { 91 | let mut i = 0; 92 | let mut end = 0; 93 | 94 | let n = self.alive.len(); 95 | while i < n { 96 | 97 | let ptr = *self.alive.get( i ); 98 | 99 | if (*ptr).is_immortal { 100 | 101 | let owned = mem::transmute::<_,Box>>( ptr ); 102 | self.immortal.push( owned ); 103 | i += 1; 104 | 105 | } else if (*ptr).marked { 106 | 107 | (*ptr).marked = false; 108 | i += 1; 109 | end += 1; 110 | 111 | } else { 112 | 113 | let mut owned = mem::transmute::<_,Box>>( ptr ); 114 | 115 | // if the rc is not 0, this was part of some cycle, 116 | // and die() was not yet called 117 | if owned.rc != 0 { 118 | owned.value.die(); 119 | } 120 | 121 | drop( owned ); 122 | i += 1; 123 | } 124 | } 125 | 126 | self.alive.truncate( end ); 127 | } 128 | } 129 | } 130 | 131 | #[unsafe_destructor] 132 | impl Drop for GarbageCollectedManager { 133 | fn drop( &mut self ) { 134 | unsafe { 135 | for &t in self.alive.iter() { 136 | let mut owned = mem::transmute::<_,Box>>( t ); 137 | 138 | if owned.rc != 0 { 139 | owned.value.die(); 140 | } 141 | 142 | drop( owned ); 143 | } 144 | } 145 | } 146 | } 147 | 148 | #[cfg(test)] 149 | mod test { 150 | 151 | use super::{GarbageCollected, GarbageCollectedManager}; 152 | 153 | struct Thing { 154 | dropped: *mut bool, 155 | } 156 | 157 | impl GarbageCollected for Thing { 158 | 159 | fn mark( &mut self ) { 160 | } 161 | } 162 | 163 | impl Drop for Thing { 164 | fn drop( &mut self ) { 165 | unsafe { 166 | *self.dropped = true; 167 | } 168 | } 169 | } 170 | 171 | #[test] 172 | fn test() { 173 | 174 | let mut things = GarbageCollectedManager::::new(); 175 | let mut dropped = false; 176 | 177 | assert!( things.alive.len() == 0 ); 178 | 179 | let thing = things.register( Thing { dropped: &mut dropped } ); 180 | assert!( things.alive.len() == 1 ); 181 | 182 | let thing2 = thing.clone(); 183 | assert!( things.alive.len() == 1 ); 184 | 185 | drop( thing ); 186 | assert!( things.alive.len() == 1 ); 187 | 188 | drop( thing2 ); 189 | assert!( things.alive.len() == 1 ); 190 | assert!( dropped == false ); 191 | 192 | things.sweep(); 193 | assert!( things.alive.len() == 0 ); 194 | assert!( dropped == true ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/libburn/parse/node.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | use mem::raw::Raw; 3 | use lang::identifier::Identifier; 4 | use vm::analysis::annotation; 5 | 6 | pub struct Root { 7 | pub statements: Vec>, 8 | pub frame: annotation::Frame, 9 | } 10 | 11 | // STATEMENTS ////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | pub enum Statement { 14 | 15 | Use { 16 | pub path: Vec, 17 | pub annotation: annotation::Use, 18 | }, 19 | 20 | ExpressionStatement { 21 | pub expression: Box, 22 | }, 23 | 24 | Assignment { 25 | pub lvalue: Box, 26 | pub rvalue: Box, 27 | }, 28 | 29 | Let { 30 | pub variable_offset: uint, 31 | pub variable_name: Identifier, 32 | pub annotation: Raw, 33 | pub default: Option>, 34 | }, 35 | 36 | Print { 37 | pub expression: Box, 38 | }, 39 | 40 | Return { 41 | pub expression: Option>, 42 | }, 43 | 44 | Throw { 45 | pub expression: Box, 46 | }, 47 | 48 | If { 49 | pub test: Box, 50 | pub block: Vec>, 51 | pub else_if_clauses: Vec>, 52 | pub else_clause: Option>, 53 | }, 54 | 55 | Try { 56 | pub block: Vec>, 57 | pub catch_clauses: Vec>, 58 | pub else_clause: Option>, 59 | pub finally_clause: Option>, 60 | }, 61 | 62 | While { 63 | pub test: Box, 64 | pub block: Vec>, 65 | pub else_clause: Option>, 66 | }, 67 | } 68 | 69 | pub struct ElseIf { 70 | pub test: Box, 71 | pub block: Vec>, 72 | } 73 | 74 | pub struct Else { 75 | pub block: Vec>, 76 | } 77 | 78 | pub struct Catch { 79 | pub type_: Option>, 80 | pub variable_name: Identifier, 81 | pub variable: Raw, 82 | pub block: Vec>, 83 | } 84 | 85 | pub struct Finally { 86 | pub block: Vec>, 87 | } 88 | 89 | // EXPRESSIONS ///////////////////////////////////////////////////////////////////////////////////// 90 | 91 | pub enum Expression { 92 | 93 | Function { 94 | pub parameters: Vec, 95 | pub frame: annotation::Frame, 96 | pub block: Vec>, 97 | }, 98 | 99 | And { 100 | pub left: Box, 101 | pub right: Box, 102 | }, 103 | Or { 104 | pub left: Box, 105 | pub right: Box, 106 | }, 107 | Not { 108 | pub expression: Box, 109 | }, 110 | 111 | Is { 112 | pub left: Box, 113 | pub right: Box, 114 | }, 115 | Eq { 116 | pub left: Box, 117 | pub right: Box, 118 | }, 119 | Neq { 120 | pub left: Box, 121 | pub right: Box, 122 | }, 123 | Lt { 124 | pub left: Box, 125 | pub right: Box, 126 | }, 127 | Gt { 128 | pub left: Box, 129 | pub right: Box, 130 | }, 131 | LtEq { 132 | pub left: Box, 133 | pub right: Box, 134 | }, 135 | GtEq { 136 | pub left: Box, 137 | pub right: Box, 138 | }, 139 | 140 | Union { 141 | pub left: Box, 142 | pub right: Box, 143 | }, 144 | 145 | Addition { 146 | pub left: Box, 147 | pub right: Box, 148 | }, 149 | Subtraction { 150 | pub left: Box, 151 | pub right: Box, 152 | }, 153 | Multiplication { 154 | pub left: Box, 155 | pub right: Box, 156 | }, 157 | Division { 158 | pub left: Box, 159 | pub right: Box, 160 | }, 161 | 162 | DotAccess { 163 | pub expression: Box, 164 | pub name: Identifier, 165 | }, 166 | 167 | ItemAccess { 168 | pub expression: Box, 169 | pub key_expression: Box, 170 | }, 171 | 172 | Call { 173 | pub expression: Box, 174 | pub arguments: Vec>, 175 | }, 176 | 177 | Variable { 178 | pub name: Identifier, 179 | pub annotation: Raw, 180 | pub source_offset: uint, 181 | }, 182 | 183 | Name { 184 | pub identifier: Identifier, 185 | pub annotation: annotation::Name, 186 | }, 187 | 188 | String { 189 | pub value: ::std::string::String, 190 | }, 191 | Integer { 192 | pub value: i64, 193 | }, 194 | Float { 195 | pub value: f64, 196 | }, 197 | Boolean { 198 | pub value: bool, 199 | }, 200 | Nothing, 201 | } 202 | 203 | pub struct FunctionParameter { 204 | pub type_: Option>, 205 | pub default: Option>, 206 | pub variable_name: Identifier, 207 | pub variable: Raw, 208 | } 209 | 210 | // LVALUES ///////////////////////////////////////////////////////////////////////////////////////// 211 | 212 | pub enum Lvalue { 213 | 214 | VariableLvalue { 215 | pub name: Identifier, 216 | pub annotation: Raw, 217 | pub source_offset: uint, 218 | }, 219 | 220 | DotAccessLvalue { 221 | pub expression: Box, 222 | pub name: Identifier, 223 | }, 224 | } 225 | -------------------------------------------------------------------------------- /src/libburn/vm/virtual_machine.rs: -------------------------------------------------------------------------------- 1 | use rustuv::uvll; 2 | use libc::c_void; 3 | use mem::gc::GarbageCollectedManager; 4 | use mem::raw::Raw; 5 | use mem::rc::Rc; 6 | use lang::origin; 7 | use lang::origin::Origin; 8 | use lang::function::Function; 9 | use lang::module::Module; 10 | use lang::value::Value; 11 | use vm::run::fiber::Fiber; 12 | use vm::error::{Error, UncaughtThrowableHandler}; 13 | use vm::repl; 14 | 15 | /// The burn VM manages memory, schedules events and runs code. 16 | /// 17 | /// Memory is garbage-collected. Collection is *not* deterministic. 18 | /// Currently the VM uses refcounting, combined with a mark-and-sweep algorithm to detect cycles. 19 | /// Some language features (e.g. module properties not being re-assignable) 20 | /// allow for interesting optimizations to this simple algorithm. 21 | /// 22 | /// Code is executed in light-weight threads called fibers. 23 | /// A fiber can suspend (`yield`) itself whenever it chooses; 24 | /// a suspended fiber can be scheduled, and continues executing where it yielded. 25 | /// Fibers are run in parallel, but never concurrently, in the rust task that calls `run`. 26 | /// This allows for safely shared memory, and concurrent IO without callbacks. 27 | pub struct VirtualMachine { 28 | #[doc(hidden)] 29 | pub functions: GarbageCollectedManager, 30 | #[doc(hidden)] 31 | pub import_paths: Vec, 32 | #[doc(hidden)] 33 | pub module_root: Box, 34 | #[doc(hidden)] 35 | pub implicit: Raw, 36 | #[doc(hidden)] 37 | uv_loop: *c_void, 38 | #[doc(hidden)] 39 | pub uncaught_throwable_handlers: Vec>, 40 | } 41 | 42 | impl VirtualMachine { 43 | 44 | /// Create a new virtual machine. 45 | pub fn new() -> VirtualMachine { 46 | 47 | let mut root = box Module::new(); 48 | let burn = box ::builtin::burn::create_module(); 49 | root.add_module( "burn", burn ); 50 | 51 | VirtualMachine { 52 | functions: GarbageCollectedManager::new(), 53 | import_paths: vec!( Path::new( "modules/" ) ), // todo! 54 | implicit: Raw::new( root.get_module( "burn" ).get_module( "implicit" ) ), 55 | module_root: root, 56 | uv_loop: unsafe { uvll::loop_new() }, 57 | uncaught_throwable_handlers: Vec::new(), 58 | } 59 | } 60 | 61 | /// Register an `UncaughtThrowableHandler`. 62 | pub fn on_uncaught_throwable( &mut self, handler: Box ) { 63 | self.uncaught_throwable_handlers.push( handler ); 64 | } 65 | 66 | /// Run scheduled events until the queue is empty. 67 | pub fn run( &mut self ) { 68 | unsafe { uvll::uv_run( self.uv_loop, uvll::RUN_ONCE ); } 69 | } 70 | 71 | /// Run scheduled events forever. Waits for new events whenever the queue is empty. 72 | pub fn run_loop( &mut self ) { 73 | unsafe { uvll::uv_run( self.uv_loop, uvll::RUN_DEFAULT ); } 74 | } 75 | 76 | /// Schedule a rust procedure to be executed. 77 | pub fn schedule( &mut self, f: proc( &mut VirtualMachine ) ) { 78 | 79 | use std::mem; 80 | 81 | let vm_ptr: *() = unsafe { mem::transmute( &*self ) }; 82 | let f_ptr: *() = unsafe { mem::transmute( box f ) }; 83 | 84 | // optimize! try using a boxed tuple instead of binding into a new proc? 85 | let f = box proc() { 86 | let vm: &mut VirtualMachine = unsafe { mem::transmute( vm_ptr ) }; 87 | let f: Box = unsafe { mem::transmute( f_ptr ) }; 88 | (*f)( vm ); 89 | }; 90 | 91 | let handle = ::rustuv::UvHandle::alloc( None::<::rustuv::AsyncWatcher>, uvll::UV_ASYNC ); 92 | unsafe { 93 | uvll::set_data_for_uv_handle( handle, &*f ); 94 | mem::forget( f ); 95 | uvll::uv_async_init( self.uv_loop, handle, callback ); 96 | uvll::uv_async_send( handle ); 97 | } 98 | 99 | extern "C" fn callback( handle: *uvll::uv_async_t ) { 100 | let f: Box = unsafe { mem::transmute( uvll::get_data_for_uv_handle( handle ) ) }; 101 | (*f)(); 102 | } 103 | } 104 | 105 | fn schedule_fiber( &mut self, fiber: Box ) { 106 | self.schedule( proc( vm ) { 107 | use vm::run::cpu; 108 | cpu::run( vm, fiber ); 109 | } ); 110 | } 111 | 112 | /// Compile some source code and schedule it for execution. 113 | /// If provided, root-level variables will be persisted in `repl_state`. 114 | /// 115 | /// Any compilation errors are returned immediately. 116 | pub fn schedule_source( &mut self, origin: Box, repl_state: Option<&mut repl::State>, source_code: &str ) -> Result<(),Vec>> { 117 | 118 | use vm::bytecode::compiler; 119 | 120 | let origin = Rc::new( origin ); 121 | let frame = try!( compiler::compile( origin, repl_state, source_code ) ); 122 | let fiber = box Fiber::new( frame ); 123 | self.schedule_fiber( fiber ); 124 | Ok( () ) 125 | } 126 | 127 | /// Convert a value into a `String` by creating and running a fiber 128 | /// to run the necessary burn code. 129 | /// This method blocks the current task until the conversion is complete. 130 | /// 131 | /// * FIXME: return uncaught throwable, if any 132 | /// * FIXME: use a separate uv_loop 133 | pub fn to_string( &mut self, value: Value ) -> Result { 134 | 135 | use vm::bytecode::code::Code; 136 | use vm::bytecode::opcode; 137 | use vm::run::frame; 138 | 139 | let mut result = box String::new(); 140 | let result_ptr = &mut *result as *mut String; 141 | 142 | let mut code = box Code::new(); 143 | code.n_local_variables = 1; 144 | code.opcodes = vec!( 145 | opcode::LoadLocal( 0 ), 146 | opcode::ToString, 147 | opcode::Return, 148 | ); 149 | 150 | let origin = box origin::Rust { name: "to_string".to_string() } as Box; 151 | 152 | let frame = frame::BurnRootFrame { 153 | origin: Rc::new( origin ), 154 | code: code, 155 | context: frame::BurnContext::new( vec!( value ), vec!() ), 156 | }; 157 | 158 | let mut fiber = box Fiber::new( frame ); 159 | fiber.on_return = Some( proc( to_string_value: Value ) { 160 | match to_string_value { 161 | ::lang::value::String( mut s ) => { 162 | unsafe { *result_ptr = s.to_string(); } 163 | } 164 | _ => { unreachable!(); } 165 | } 166 | } ); 167 | 168 | self.schedule_fiber( fiber ); 169 | self.run(); 170 | 171 | Ok( *result ) 172 | } 173 | } 174 | 175 | #[unsafe_destructor] 176 | impl Drop for VirtualMachine { 177 | fn drop( &mut self ) { 178 | unsafe { uvll::uv_loop_delete( self.uv_loop ); } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/bin/burn.rs: -------------------------------------------------------------------------------- 1 | #![feature(macro_rules)] 2 | 3 | extern crate burn; 4 | 5 | use std::os; 6 | use std::io; 7 | use std::path::posix::Path; 8 | use burn::lang::origin; 9 | use burn::lang::Value; 10 | use burn::vm::{VirtualMachine, Error, UncaughtThrowableHandler}; 11 | use burn::repl; 12 | use burn::util; 13 | 14 | macro_rules! errln( 15 | ($($arg:tt)*) => ( let _ = ::std::io::stderr().write_line( format!($($arg)*).as_slice() ) ) 16 | ) 17 | 18 | static HELP: &'static str = 19 | "usage: burn [options...] [args...] 20 | 21 | Read and run burn program from file. Use - to read from stdin. 22 | 23 | options: 24 | -d | --debug Print bytecode and instruction info. 25 | -h | --help Print this help message."; 26 | 27 | fn main() { 28 | 29 | let mut burn = Burn { 30 | verbose: true, 31 | }; 32 | 33 | enum Input { 34 | Stdin, 35 | File( Path ), 36 | Repl, 37 | } 38 | 39 | let mut input = Repl; 40 | let mut args = os::args().move_iter().skip(1); 41 | 42 | for arg in args { 43 | match arg.as_slice() { 44 | "-d" | "--debug" => { 45 | unsafe { burn::DEBUG = true; } 46 | } 47 | "-h" | "--help" => { 48 | let _ = io::stdout().write_line( HELP ); 49 | return; 50 | } 51 | "-q" | "--quiet" => { 52 | burn.verbose = false; 53 | } 54 | "-" => { 55 | input = Stdin; 56 | break; 57 | } 58 | _ => { 59 | input = File( Path::new( arg ) ); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | let remaining_args = args.collect::>(); 66 | 67 | match input { 68 | Stdin => { burn.run_stdin( remaining_args ); } 69 | File( path ) => { burn.run_script( path, remaining_args ); } 70 | Repl => { burn.run_repl( remaining_args ); } 71 | } 72 | } 73 | 74 | struct Burn { 75 | verbose: bool, 76 | } 77 | 78 | impl Burn { 79 | 80 | fn run_stdin( &self, args: Vec ) { 81 | 82 | (args); 83 | 84 | let mut vm = VirtualMachine::new(); 85 | vm.on_uncaught_throwable( box OsStatusUpdater as Box ); 86 | vm.on_uncaught_throwable( box ErrorPrinter as Box ); 87 | 88 | let origin = box origin::Stdin as Box; 89 | 90 | let source = match io::stdin().read_to_str() { 91 | Ok( source ) => source, 92 | Err( m ) => { 93 | errln!( "Error reading {}: {}", origin, m ); 94 | os::set_exit_status( 1 ); 95 | return; 96 | } 97 | }; 98 | 99 | match vm.schedule_source( origin, None, source.as_slice() ) { 100 | 101 | Ok( () ) => { 102 | vm.run(); 103 | } 104 | 105 | Err( errors ) => { 106 | os::set_exit_status( 1 ); 107 | for error in errors.move_iter() { 108 | self.print_libburn_error( source.as_slice(), error ); 109 | } 110 | } 111 | }; 112 | } 113 | 114 | fn run_script( &self, path: Path, args: Vec ) { 115 | 116 | (args); 117 | 118 | let mut vm = VirtualMachine::new(); 119 | vm.on_uncaught_throwable( box OsStatusUpdater as Box ); 120 | vm.on_uncaught_throwable( box ErrorPrinter as Box ); 121 | 122 | let origin = box origin::Script { path: path } as Box; 123 | 124 | let mut file = match io::File::open( origin.get_path().unwrap() ) { 125 | Ok( f ) => f, 126 | Err( m ) => { 127 | errln!( "Error opening {}: {}", origin, m ); 128 | os::set_exit_status( 1 ); 129 | return; 130 | } 131 | }; 132 | 133 | let source = match file.read_to_str() { 134 | Ok( source ) => source, 135 | Err( m ) => { 136 | errln!( "Error reading {}: {}", origin, m ); 137 | os::set_exit_status( 1 ); 138 | return; 139 | } 140 | }; 141 | 142 | match vm.schedule_source( origin, None, source.as_slice() ) { 143 | 144 | Ok( () ) => { 145 | vm.run(); 146 | } 147 | 148 | Err( errors ) => { 149 | os::set_exit_status( 1 ); 150 | for error in errors.move_iter() { 151 | self.print_libburn_error( source.as_slice(), error ); 152 | } 153 | } 154 | }; 155 | } 156 | 157 | fn run_repl( &self, args: Vec ) { 158 | 159 | (args); 160 | 161 | let mut vm = VirtualMachine::new(); 162 | vm.on_uncaught_throwable( box ErrorPrinter as Box ); 163 | let mut state = repl::State::new(); 164 | 165 | loop { 166 | 167 | let mut input = String::new(); 168 | loop { 169 | print!( "> " ); 170 | let line = match io::stdin().read_line() { 171 | Ok( l ) => l, 172 | Err( e ) => { 173 | errln!( "Error reading stdin: {}", e ); 174 | os::set_exit_status( 1 ); 175 | return; 176 | } 177 | }; 178 | 179 | if line.as_slice() == "\n" { 180 | break; 181 | } else { 182 | input.push_str( line.as_slice() ); 183 | } 184 | } 185 | 186 | let origin = box origin::Stdin as Box; 187 | match vm.schedule_source( origin, Some( &mut state ), input.as_slice() ) { 188 | Ok( () ) => { 189 | vm.run(); 190 | } 191 | Err( errors ) => { 192 | for error in errors.move_iter() { 193 | self.print_libburn_error( input.as_slice(), error ); 194 | } 195 | } 196 | } 197 | } 198 | } 199 | 200 | fn print_libburn_error( &self, source: &str, error: Box ) { 201 | 202 | let line = util::source::find_line( source, error.get_source_offset() ); 203 | 204 | errln!( "{}", error.get_message() ); 205 | errln!( "in {} on line {}", error.get_origin().get_name(), line.no ); 206 | 207 | if self.verbose { 208 | self.print_error_line_fragment( source, line ); 209 | } 210 | } 211 | 212 | fn print_error_line_fragment( &self, source: &str, line: util::source::Line ) { 213 | errln!( "{}", source.slice( line.start, line.end ) ); 214 | for c in source.slice( line.start, line.offset ).chars() { 215 | let _ = io::stderr().write_char( if c == '\t' { c } else { ' ' } ); 216 | } 217 | errln!( "^" ); 218 | } 219 | } 220 | 221 | struct ErrorPrinter; 222 | 223 | impl UncaughtThrowableHandler for ErrorPrinter { 224 | fn handle_uncaught_throwable( &mut self, vm: &mut VirtualMachine, t: Value ) { 225 | errln!( "Uncaught throwable:\n{}", vm.to_string( t ).ok().unwrap() ); 226 | } 227 | } 228 | 229 | struct OsStatusUpdater; 230 | 231 | impl UncaughtThrowableHandler for OsStatusUpdater { 232 | fn handle_uncaught_throwable( &mut self, _: &mut VirtualMachine, _: Value ) { 233 | os::set_exit_status( 2 ); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/libburn/lang/operations.rs: -------------------------------------------------------------------------------- 1 | use lang::identifier::Identifier; 2 | use lang::value; 3 | use lang::value::Value; 4 | use builtin::burn; 5 | use builtin::burn::errors::create_type_error; 6 | use mem::rc::Rc; 7 | use vm::run::rust; 8 | 9 | pub fn is_truthy( value: &Value ) -> bool { 10 | match *value { 11 | 12 | value::Nothing => false, 13 | value::Boolean( b ) => b, 14 | value::Integer( i ) => i != 0, 15 | value::Float( f ) => f != 0f64, 16 | value::String( ref s ) => s.len() > 0, 17 | 18 | value::Function(..) 19 | | value::TypeUnion(..) 20 | | value::TypeIntersection(..) 21 | | value::Module(..) 22 | => true, 23 | 24 | value::StaticSpecial(..) => true, 25 | value::RcSpecial( ref r ) => r.is_truthy(), 26 | } 27 | } 28 | 29 | pub fn repr( value: &Value ) -> String { 30 | match *value { 31 | 32 | value::Nothing => "".to_string(), 33 | value::Boolean(..) => "".to_string(), 34 | value::Integer(..) => "".to_string(), 35 | value::Float(..) => "".to_string(), 36 | value::String(..) => "".to_string(), 37 | 38 | value::Function(..) => "".to_string(), 39 | value::TypeUnion(..) | value::TypeIntersection(..) => "".to_string(), 40 | value::Module(..) => "".to_string(), 41 | 42 | value::StaticSpecial( special ) => special.repr(), 43 | value::RcSpecial( ref r ) => r.repr(), 44 | } 45 | } 46 | 47 | pub fn to_string( value: &Value ) -> rust::Result { 48 | rust::Ok( value::String( 49 | match *value { 50 | 51 | value::Nothing => Rc::new( "nothing".into_string() ), 52 | value::Boolean( true ) => Rc::new( "true".into_string() ), 53 | value::Boolean( false ) => Rc::new( "false".into_string() ), 54 | value::Integer( i ) => Rc::new( format!( "{}", i ) ), 55 | value::Float( f ) => Rc::new( format!( "{}", f ) ), 56 | value::String( ref s ) => s.clone(), 57 | 58 | value::StaticSpecial( special ) => Rc::new( special.repr() ), 59 | value::RcSpecial( ref r ) => Rc::new( r.to_string() ), 60 | 61 | _ => { Rc::new( repr( value ) ) } 62 | } 63 | ) ) 64 | } 65 | 66 | pub fn add( left: &Value, right: &Value ) -> rust::Result { 67 | match *left { 68 | 69 | value::Integer( l ) => { 70 | match *right { 71 | value::Integer( r ) => { return rust::Ok( value::Integer( l + r ) ); } 72 | value::Float( r ) => { return rust::Ok( value::Float( l as f64 + r ) ); } 73 | _ => {} 74 | } 75 | } 76 | 77 | value::Float( l ) => { 78 | match *right { 79 | value::Integer( r ) => { return rust::Ok( value::Float( l + r as f64 ) ); } 80 | value::Float( r ) => { return rust::Ok( value::Float( l + r ) ); } 81 | _ => {} 82 | } 83 | } 84 | 85 | _ => {} 86 | } 87 | 88 | return rust::Throw( 89 | create_type_error( format!( "Can't add {} and {}", repr( left ), repr( right ) ) ) 90 | ); 91 | } 92 | 93 | pub fn subtract( left: &Value, right: &Value ) -> rust::Result { 94 | match *left { 95 | 96 | value::Integer( l ) => { 97 | match *right { 98 | value::Integer( r ) => { return rust::Ok( value::Integer( l - r ) ); } 99 | value::Float( r ) => { return rust::Ok( value::Float( l as f64 - r ) ); } 100 | _ => {} 101 | } 102 | } 103 | 104 | value::Float( l ) => { 105 | match *right { 106 | value::Integer( r ) => { return rust::Ok( value::Float( l - r as f64 ) ); } 107 | value::Float( r ) => { return rust::Ok( value::Float( l - r ) ); } 108 | _ => {} 109 | } 110 | } 111 | 112 | _ => {} 113 | } 114 | 115 | return rust::Throw( 116 | create_type_error( format!( "Can't subtract {} and {}", repr( left ), repr( right ) ) ) 117 | ); 118 | } 119 | 120 | pub fn multiply( left: &Value, right: &Value ) -> rust::Result { 121 | return rust::Throw( 122 | create_type_error( format!( "Can't multiply {} and {}", repr( left ), repr( right ) ) ) 123 | ); 124 | } 125 | 126 | pub fn divide( left: &Value, right: &Value ) -> rust::Result { 127 | return rust::Throw( 128 | create_type_error( format!( "Can't divide {} and {}", repr( left ), repr( right ) ) ) 129 | ); 130 | } 131 | 132 | pub fn union( left: Value, right: Value ) -> rust::Result { 133 | 134 | if ! burn::types::is_type( &left ) { 135 | return rust::Throw( 136 | create_type_error( format!( "Can't create type union: {} is not a type", repr( &left ) ) ) 137 | ); 138 | } 139 | 140 | if ! burn::types::is_type( &right ) { 141 | return rust::Throw( 142 | create_type_error( format!( "Can't create type union: {} is not a type", repr( &right ) ) ) 143 | ); 144 | } 145 | 146 | rust::Ok( value::TypeUnion( Rc::new( ::lang::type_::TypeUnion::new( left, right ) ) ) ) 147 | } 148 | 149 | pub fn is( value: &Value, type_: &Value ) -> rust::Result { 150 | match *type_ { 151 | 152 | value::TypeUnion( ref r ) => { 153 | return match is( value, &r.left ) { 154 | rust::Ok( value::Boolean( false ) ) => is( value, &r.right ), 155 | other_result @ _ => other_result, 156 | } 157 | } 158 | 159 | value::StaticSpecial( special ) => { 160 | if special.is_type() { 161 | return rust::Ok( value::Boolean( special.type_test( value ) ) ) 162 | } 163 | } 164 | 165 | value::RcSpecial( ref r ) => { 166 | if r.is_type() { 167 | return rust::Ok( value::Boolean( r.type_test( value ) ) ) 168 | } 169 | } 170 | 171 | _ => {} 172 | } 173 | 174 | return rust::Throw( 175 | create_type_error( format!( "{} is not a type", repr( type_ ) ) ) 176 | ); 177 | } 178 | 179 | pub fn eq( left: &Value, right: &Value ) -> rust::Result { 180 | return rust::Throw( 181 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 182 | ); 183 | } 184 | 185 | pub fn neq( left: &Value, right: &Value ) -> rust::Result { 186 | return rust::Throw( 187 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 188 | ); 189 | } 190 | 191 | pub fn lt( left: &Value, right: &Value ) -> rust::Result { 192 | return rust::Throw( 193 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 194 | ); 195 | } 196 | 197 | pub fn gt( left: &Value, right: &Value ) -> rust::Result { 198 | return rust::Throw( 199 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 200 | ); 201 | } 202 | 203 | pub fn lt_eq( left: &Value, right: &Value ) -> rust::Result { 204 | return rust::Throw( 205 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 206 | ); 207 | } 208 | 209 | pub fn gt_eq( left: &Value, right: &Value ) -> rust::Result { 210 | return rust::Throw( 211 | create_type_error( format!( "Can't compare {} and {}", repr( left ), repr( right ) ) ) 212 | ); 213 | } 214 | 215 | pub fn get_property( accessed: &Value, name: Identifier ) -> rust::Result { 216 | (accessed); (name); 217 | unimplemented!(); 218 | } 219 | 220 | pub fn set_property( accessed: &Value, name: Identifier, value: &Value ) -> rust::Result { 221 | (accessed); (name); (value); 222 | unimplemented!(); 223 | } 224 | -------------------------------------------------------------------------------- /src/libburn/lang/module.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io::File; 3 | use serialize::{json, Decodable}; 4 | use mem::raw::Raw; 5 | use mem::rc::Rc; 6 | use lang::origin; 7 | use lang::origin::Origin; 8 | use lang::identifier::Identifier; 9 | use lang::value; 10 | use lang::value::Value; 11 | use vm::bytecode::code::Code; 12 | use vm::bytecode::compiler; 13 | use vm::bytecode::opcode; 14 | use vm::run::rust; 15 | use vm::run::rust::Operation; 16 | use vm::virtual_machine::VirtualMachine; 17 | 18 | pub struct Module { 19 | modules: HashMap>, 20 | contents: HashMap, 21 | locked: bool, 22 | } 23 | 24 | impl Module { 25 | pub fn new() -> Module { 26 | Module { 27 | modules: HashMap::new(), 28 | contents: HashMap::new(), 29 | locked: false, 30 | } 31 | } 32 | 33 | pub fn add_module( &mut self, name: &'static str, module: Box ) { 34 | self.add_module_with_id( Identifier::find_or_create( name ), module ); 35 | } 36 | 37 | pub fn add_module_with_id( &mut self, name: Identifier, module: Box ) { 38 | assert!( ! self.locked ); 39 | self.contents.insert( name, value::Module( Raw::new( module ) ) ); 40 | self.modules.insert( name, module ); 41 | } 42 | 43 | pub fn add( &mut self, name: &'static str, value: Value ) { 44 | self.add_with_id( Identifier::find_or_create( name ), value ); 45 | } 46 | 47 | pub fn add_with_id( &mut self, name: Identifier, value: Value ) { 48 | assert!( ! self.locked ); 49 | self.contents.insert( name, value ); 50 | } 51 | 52 | pub fn has( &self, name: &'static str ) -> bool { 53 | self.has_id( Identifier::find_or_create( name ) ) 54 | } 55 | 56 | pub fn has_id( &self, name: Identifier ) -> bool { 57 | self.contents.contains_key( &name ) 58 | } 59 | 60 | pub fn lock( &mut self ) { 61 | assert!( ! self.locked ); 62 | self.locked = true 63 | } 64 | 65 | pub fn find_id( &self, identifier: Identifier ) -> Result { 66 | match self.contents.find( &identifier ) { 67 | Some( value ) => Ok( value.clone() ), 68 | None => Err( value::Nothing ), // todo! add a real error 69 | } 70 | } 71 | 72 | pub fn get( &self, name: &'static str ) -> Value { 73 | self.get_id( Identifier::find_or_create( name ) ) 74 | } 75 | 76 | pub fn get_id( &self, name: Identifier ) -> Value { 77 | match self.contents.find( &name ) { 78 | Some( value ) => value.clone(), 79 | None => { fail!(); }, 80 | } 81 | } 82 | 83 | pub fn get_module<'l>( &'l mut self, name: &'static str ) -> &'l mut Module { 84 | match self.modules.find_mut( &Identifier::find_or_create( name ) ) { 85 | Some( module ) => &mut **module, 86 | None => { fail!(); }, 87 | } 88 | } 89 | } 90 | 91 | #[deriving(Decodable)] 92 | pub struct MetaData { 93 | sources: Vec, 94 | } 95 | 96 | pub struct Use { 97 | fqn: Vec, 98 | inlines: Vec<(Raw, uint)>, 99 | step: UseOperationStep, 100 | root_sources: Vec, 101 | loaded: Value, 102 | } 103 | 104 | impl Use { 105 | 106 | pub fn new( fqn: Vec ) -> Use { 107 | Use { 108 | fqn: fqn, 109 | inlines: Vec::new(), 110 | step: FindRoot, 111 | root_sources: Vec::new(), 112 | loaded: value::Nothing, 113 | } 114 | } 115 | 116 | pub fn add_inline( &mut self, code: Raw, offset: uint ) { 117 | self.inlines.push( (code, offset) ); 118 | } 119 | } 120 | 121 | enum UseOperationStep { 122 | FindRoot, 123 | ImportRoot, 124 | ImportSubs, 125 | Inline, 126 | } 127 | 128 | impl Operation for Use { 129 | fn run( &mut self, vm: &mut VirtualMachine, value: Result ) -> rust::Result { 130 | 'step_loop: loop { 131 | match self.step { 132 | 133 | FindRoot => { 134 | let mut module_name = self.fqn.shift().unwrap(); 135 | 136 | if vm.module_root.has_id( module_name ) { 137 | 138 | self.loaded = vm.module_root.get_id( module_name ); 139 | self.step = ImportSubs; 140 | 141 | } else { 142 | 143 | for import_path in vm.import_paths.iter() { 144 | 145 | let mut suspect = import_path.clone(); 146 | suspect.push( format!( "{}/burn_module.json", module_name.get_value() ) ); 147 | 148 | if suspect.exists() { 149 | 150 | let module = box Module::new(); 151 | self.loaded = value::Module( Raw::new( module ) ); 152 | vm.module_root.add_module_with_id( module_name, module ); 153 | 154 | // todo! handle errors 155 | let mut meta_file = File::open( &suspect ).unwrap(); 156 | let meta_data = json::from_reader( &mut meta_file ).unwrap(); 157 | let mut decoder = json::Decoder::new( meta_data ); 158 | let meta_struct: MetaData = Decodable::decode( &mut decoder ).unwrap(); 159 | 160 | for source in meta_struct.sources.move_iter() { 161 | let mut source_path = import_path.clone(); 162 | source_path.push( format!( "{}/{}", module_name.get_value(), source ) ); 163 | self.root_sources.push( source_path ); 164 | } 165 | 166 | self.step = ImportRoot; 167 | continue 'step_loop; 168 | } 169 | } 170 | 171 | return rust::Throw( value::Integer( 3 ) ); 172 | } 173 | } 174 | 175 | ImportRoot => { 176 | match self.root_sources.shift() { 177 | None => { 178 | self.step = ImportSubs; 179 | } 180 | Some( path ) => { 181 | 182 | let script = box origin::Script { path: path }; 183 | let source = ::std::io::File::open( &script.path ).unwrap().read_to_str().unwrap(); // todo! handle errors 184 | let origin = script as Box; 185 | 186 | return match compiler::compile( Rc::new( origin ), None, source.as_slice() ) { 187 | Ok( frame ) => rust::Burn( frame ), 188 | Err( errors ) => { 189 | (errors); 190 | return rust::Throw( value::Nothing ); // todo! add a real error 191 | } 192 | } 193 | } 194 | } 195 | } 196 | 197 | ImportSubs => { 198 | 199 | match value { 200 | Ok( value::Nothing ) => {}, 201 | Ok( _ ) => { unreachable!(); }, 202 | Err( t ) => { 203 | return rust::Throw( t ); 204 | } 205 | }; 206 | 207 | match self.fqn.shift() { 208 | None => { 209 | self.step = Inline; 210 | } 211 | Some( name ) => { 212 | (name); 213 | unimplemented!(); 214 | } 215 | } 216 | } 217 | 218 | Inline => { 219 | 220 | let opcode = match self.loaded { 221 | value::Module( m ) => opcode::InlinedModule { ptr: m }, 222 | _ => { unimplemented!(); } 223 | }; 224 | 225 | for &(mut code, offset) in self.inlines.mut_iter() { 226 | *code.opcodes.get_mut( offset ) = opcode; 227 | } 228 | 229 | return rust::Ok( value::Nothing ); 230 | } 231 | } 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/libburn/vm/analysis/allocation.rs: -------------------------------------------------------------------------------- 1 | use parse::node; 2 | use vm::analysis::annotation; 3 | use vm::repl; 4 | 5 | struct Frame { 6 | n_local_variables: uint, 7 | n_shared_local_variables: uint, 8 | n_static_bound_variables: uint, 9 | n_shared_bound_variables: uint, 10 | } 11 | 12 | pub struct AnalyzeAllocation { 13 | frames: Vec, 14 | } 15 | 16 | impl AnalyzeAllocation { 17 | 18 | pub fn new() -> AnalyzeAllocation { 19 | AnalyzeAllocation { 20 | frames: Vec::new(), 21 | } 22 | } 23 | 24 | pub fn analyze_root( 25 | &mut self, 26 | root: &mut node::Root, 27 | repl_state: &mut Option<&mut repl::State> 28 | ) { 29 | 30 | let n_repl_vars = match *repl_state { 31 | Some( ref repl_state ) => repl_state.variables.len(), 32 | None => 0, 33 | }; 34 | 35 | self.analyze_frame_with_n_repl_vars( 36 | &mut root.frame, 37 | n_repl_vars 38 | ); 39 | } 40 | 41 | fn analyze_frame( &mut self, frame: &mut annotation::Frame ) { 42 | self.analyze_frame_with_n_repl_vars( frame, 0 ); 43 | } 44 | 45 | fn analyze_frame_with_n_repl_vars( 46 | &mut self, 47 | frame: &mut annotation::Frame, 48 | n_repl_vars: uint 49 | ) { 50 | self.push_frame(); 51 | 52 | for variable in frame.declared_variables.mut_iter().take( n_repl_vars ) { 53 | variable.local_storage_type = annotation::storage::SharedLocal; 54 | variable.bound_storage_type = annotation::storage::SharedBound; 55 | } 56 | 57 | for variable in frame.declared_variables.mut_iter().skip( n_repl_vars ) { 58 | self.determine_variable_storage_types( *variable ); 59 | } 60 | 61 | for variable in frame.declared_variables.mut_iter() { 62 | self.determine_declared_variable_storage_index( *variable ); 63 | } 64 | 65 | for &mut function in frame.functions.iter() { 66 | match_enum!( *function.deref_mut() to node::Function { 67 | frame: ref mut frame, 68 | .. 69 | } => { 70 | self.analyze_frame( frame ); 71 | } ); 72 | } 73 | 74 | frame.n_local_variables = self.get_current_frame().n_local_variables; 75 | frame.n_shared_local_variables = self.get_current_frame().n_shared_local_variables; 76 | 77 | if frame.closure.is_some() { 78 | self.analyze_closure( frame.closure.as_mut().unwrap() ); 79 | } 80 | 81 | self.pop_frame(); 82 | } 83 | 84 | fn analyze_closure( &mut self, closure: &mut annotation::Closure ) { 85 | 86 | for binding in closure.bindings.mut_iter() { 87 | self.determine_binding_storage_index( binding ); 88 | } 89 | 90 | closure.n_static_bound_variables = self.get_current_frame().n_static_bound_variables; 91 | closure.n_shared_bound_variables = self.get_current_frame().n_shared_bound_variables; 92 | } 93 | 94 | fn determine_variable_storage_types( &mut self, variable: &mut annotation::Variable ) { 95 | match variable.root_binds.len() { 96 | 97 | 0 => { 98 | variable.local_storage_type = annotation::storage::Local; 99 | } 100 | 101 | 1 => { 102 | let only_bind = variable.root_binds.get(0); 103 | 104 | for write in variable.writes.iter() { 105 | if write.time > only_bind.time { 106 | // The variable is assigned to after binding. 107 | variable.local_storage_type = annotation::storage::SharedLocal; 108 | variable.bound_storage_type = annotation::storage::SharedBound; 109 | return; 110 | } 111 | } 112 | 113 | if ! only_bind.mutable { 114 | // The variable is never assigned to after binding. 115 | // It is effectively immutable! 116 | variable.local_storage_type = annotation::storage::Local; 117 | variable.bound_storage_type = annotation::storage::StaticBound; 118 | return; 119 | } 120 | 121 | for read in variable.reads.iter() { 122 | if read.time > only_bind.time { 123 | // The variable is assigned to inside the binding function, 124 | // but also read after binding. 125 | variable.local_storage_type = annotation::storage::SharedLocal; 126 | variable.bound_storage_type = annotation::storage::SharedBound; 127 | return; 128 | } 129 | } 130 | 131 | if ! variable.n_binds == 1 { 132 | // In the declaring frame, the variable is dead after binding. 133 | // The binding function mutates the variable, 134 | // but since it is the only owner of the value, it can be static. 135 | variable.local_storage_type = annotation::storage::Local; 136 | variable.bound_storage_type = annotation::storage::StaticBound; 137 | } 138 | 139 | // In the declaring frame, the variable is dead after binding, so it can be local. 140 | // The binding functions have to share, since they mutate the variable. 141 | variable.local_storage_type = annotation::storage::Local; 142 | variable.bound_storage_type = annotation::storage::SharedBound; 143 | } 144 | 145 | _ => { 146 | 147 | for bind in variable.root_binds.iter() { 148 | if bind.mutable { 149 | // Multiple bindings and at least one mutates. 150 | variable.local_storage_type = annotation::storage::SharedLocal; 151 | variable.bound_storage_type = annotation::storage::SharedBound; 152 | } 153 | } 154 | 155 | let first_binding = variable.root_binds.get(0); 156 | 157 | for write in variable.writes.iter() { 158 | if write.time > first_binding.time { 159 | // The variable is assigned to after a binding. 160 | variable.local_storage_type = annotation::storage::SharedLocal; 161 | variable.bound_storage_type = annotation::storage::SharedBound; 162 | return; 163 | } 164 | } 165 | 166 | // The variable is never assigned to after binding. 167 | // It is effectively immutable! 168 | variable.local_storage_type = annotation::storage::Local; 169 | variable.bound_storage_type = annotation::storage::StaticBound; 170 | } 171 | } 172 | } 173 | 174 | fn determine_declared_variable_storage_index( &mut self, variable: &mut annotation::Variable ) { 175 | match variable.local_storage_type { 176 | 177 | annotation::storage::Local => { 178 | variable.local_storage_index = self.get_current_frame().n_local_variables; 179 | self.get_current_frame().n_local_variables += 1; 180 | } 181 | 182 | annotation::storage::SharedLocal => { 183 | variable.local_storage_index = self.get_current_frame().n_shared_local_variables; 184 | self.get_current_frame().n_shared_local_variables += 1; 185 | } 186 | } 187 | } 188 | 189 | fn determine_binding_storage_index( &mut self, binding: &mut annotation::Binding ) { 190 | match binding.variable.bound_storage_type { 191 | 192 | annotation::storage::StaticBound => { 193 | binding.storage_index = self.get_current_frame().n_static_bound_variables; 194 | self.get_current_frame().n_static_bound_variables += 1; 195 | } 196 | 197 | annotation::storage::SharedBound => { 198 | binding.storage_index = self.get_current_frame().n_shared_bound_variables; 199 | self.get_current_frame().n_shared_bound_variables += 1; 200 | } 201 | } 202 | } 203 | 204 | // 205 | // Helpers 206 | // 207 | 208 | fn push_frame( &mut self ) { 209 | self.frames.push( Frame { 210 | n_local_variables: 0, 211 | n_shared_local_variables: 0, 212 | n_static_bound_variables: 0, 213 | n_shared_bound_variables: 0, 214 | } ); 215 | } 216 | 217 | fn pop_frame( &mut self ) { 218 | self.frames.pop(); 219 | } 220 | 221 | fn get_current_frame<'l>( &'l mut self ) -> &'l mut Frame { 222 | self.frames.mut_last().unwrap() 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/doc/reference.md: -------------------------------------------------------------------------------- 1 | % Burn - Language specification 2 | 3 | About 4 | ==== 5 | 6 | This document tries to describe Burn programs lexically, gramatically and semantically. 7 | I try to be clear and precise, without getting too formal. 8 | Since this is probably the only documentation for the foreseeable future, it also includes examples, 9 | hints, opinions, conventions and other things that aren't strictly part of a language specification document. 10 | 11 | Burn is still under development. Everything here is subject to change. 12 | A lot of things aren't implemented yet. Some things are implemented, but not documented. 13 | 14 | My name is Alex. If you have questions or comments, send an e-mail to . 15 | 16 | 17 | 18 | Burn in a nutshell 19 | ================ 20 | 21 | Burn intends to be a light-weight, general-purpose programming language. 22 | Here are some of its properties you're likely to know from existing languages: 23 | 24 | * Newlines separate statements. Curly braces are mandatory. 25 | * Block scoping. 26 | * Values have types, variables don't. Values are not implicitely coerced. 27 | * Functions are first-class values and can have free variables (lexical closure). 28 | * Object-oriented programming with multiple inheritance. Classes are first-class values too, and support lexical closure. 29 | * Memory is garbage-collected. 30 | * Concurrency is achieved through fibers. 31 | * It compiles to bytecode at runtime. This bytecode is (to some small degree) self-optimizing. 32 | 33 | \ 34 | 35 | Of course there's more to Burn than that. Here are some of the twists that really set Burn apart: 36 | 37 | \ 38 | 39 | **Typing** 40 | 41 | Burn's types are more like mathematical sets than the type systems you are probably familiar with. 42 | Think Venn diagrams rather than family trees. 43 | It has the usual types like `Integer` or `String`, but also `Positive` or `Empty`. 44 | 45 | There are a lot of types in the standard library, but you can create your own as well. 46 | Types are first-class values and can be combined and created ad hoc: 47 | 48 | ``` 49 | let $Real = Integer | Float 50 | let $NonEmptyString = String + not Empty 51 | let $HttpUrl = String.starting_with( "http://" ) 52 | let $CustomType = function( $value ) { return } 53 | ``` 54 | 55 | \ 56 | 57 | **Object capabilities** 58 | 59 | Burn follows an object-capability model. 60 | Capabilities are functions or objects that can do things with side-effects (e.g. read a file or print to stdout). 61 | All capabilities are given to the program entry point (i.e. `main`), and it is then responsible for passing them to other parts of the program. 62 | This is basically nothing more than language-enforced dependency injection. 63 | 64 | * You can, at any time, safely import a library or load a script. It can only have side-effects if and when you give it the required capabilities. 65 | * An untrusted Burn program can be run with reduced capabilities, or by lazily prompting them from the user. 66 | * You can safely embed Burn without cutting off access to third party libraries by simply giving it reduced capabilities. 67 | * Burn programs are probably more likely to be reusable and testable. 68 | 69 | 70 | 71 | Lexical structure 72 | ================= 73 | 74 | ## Comments 75 | 76 | Burn supports block and line comments. 77 | 78 |
79 | 80 | Block comments start with a `/` followed by one or more `*`. 81 | They end with the exact same amount of stars followed by a `/`, or at the end of the file. 82 | 83 |
84 | 85 | ``` 86 | /* One block comment */ 87 | 88 | /** Another **/ 89 | 90 | /* I don't end here: **/ 91 | I end here: */ 92 | 93 | /* 94 | I end at the end of the file... 95 | ``` 96 | 97 |
98 | 99 |
100 | 101 | Block comments can't actually be nested. 102 | One block comment can, incidentally, contain another if it has a different amount of stars. 103 | 104 |
105 | 106 | ``` 107 | /** 108 | Block comment containing 109 | /* Another block comment */ 110 | **/ 111 | ``` 112 | 113 |
114 | 115 |
116 | 117 | Line comments start with `//` and end at the first newline. 118 | 119 |
120 | 121 | ``` 122 | // Line comment 123 | print "Hello" // Be polite! 124 | ``` 125 | 126 |
127 | 128 |
129 | There are no doc comments or attributes at this time. 130 | A generalized annotation syntax is in the works. 131 |
132 | 133 | ## Whitespace 134 | 135 | Spaces and tabs are not significant, besides separating tokens. 136 | 137 | ## Newlines 138 | 139 | Newlines *are* significant. They separate statements: 140 | 141 | ``` 142 | statement 143 | if expression { 144 | statement 145 | statement 146 | } 147 | ``` 148 | 149 | This makes burn code easy to read and write. 150 | Sometimes you might want to use newlines to break long expressions and constructs, so they are ignored... 151 | 152 | * After binary operators. 153 | * Within parenthesized expressions. 154 | * At the start or end of blocks. 155 | * After keywords, until the end of the expression or construct, but not within their blocks. 156 | 157 | ## Symbols 158 | 159 | ```grammar 160 | { } ( ) [ ] 161 | , -> . 162 | < > == != <= >= 163 | + - * / % | 164 | += -= *= /= %= 165 | ``` 166 | 167 | ## Keywords 168 | 169 | Keywords have special meaning and can't be used where an identifier is expected. 170 | 171 | ```grammar 172 | and 173 | catch class 174 | else extends 175 | false finally for function 176 | if in is 177 | let 178 | new not nothing 179 | or 180 | return 181 | this throw true try 182 | while 183 | ``` 184 | 185 | ## Identifiers 186 | 187 |
188 | 189 | Identifiers are composed of one or more letters, digits, `_`, `:` or `!`. 190 | The first character can not be a digit. Any other combination is allowed. 191 | 192 | Identifiers are case-sensitive. 193 | 194 | `:` should be used sparingly. `!` should be used *especially sparingly*. 195 | 196 |
197 | 198 | ```grammar 199 | [A-Za-z_:!][A-Za-z0-9_:!]* 200 | ``` 201 | 202 |
203 | 204 | ## Variables 205 | 206 |
207 | 208 | Variables always start with a `$` and are followed by one or more letters, digits, `_`, `:` or `!`. 209 | 210 |
211 | 212 | ```grammar 213 | \$[A-Za-z_:!]+ 214 | ``` 215 | 216 |
217 | 218 | ## Literals 219 | 220 | 221 | Statements 222 | ========== 223 | 224 | ## Simple statements 225 | 226 | ```grammar 227 | lvalue := 228 | variable 229 | | dot_expression 230 | | item_expression 231 | | lvalue_tuple 232 | 233 | lvalue_tuple := `(` ( lvalue `,` )+ [ lvalue ] `)` 234 | ``` 235 | 236 | ### Assignment 237 | 238 | #### Augmented assignment operators 239 | 240 | ### Expression statement 241 | 242 | ### Import statement 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | ## Control flow statements 252 | 253 | ```grammar 254 | if_statement := 255 | `if` expression block 256 | ( `else` `if` expression block )* 257 | [ `else` block ] 258 | 259 | while_statement := 260 | `while` expression block 261 | [ `else` block ] 262 | 263 | for_in_statement := 264 | `for` lvalue `in` expression block 265 | [ `else` block ] 266 | ``` 267 | 268 | ### If statement 269 | 270 | ### While statement 271 | 272 | ### For-in statement 273 | 274 | ### Break statement 275 | 276 | ### Continue statement 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | ## Throwing flow 285 | 286 | ```grammar 287 | try_statement := 288 | `try` block 289 | ( `catch` `(` [ type ] variable `)` block )* 290 | [ `else` block ] 291 | [ `finally` block ] 292 | 293 | throw_statement := `throw` expression 294 | ``` 295 | 296 | ### Try statement 297 | 298 | ### Throw statement 299 | 300 | 301 | 302 | 303 | # Expressions 304 | 305 | ## Simple expressions 306 | 307 | ```grammar 308 | simple_expression := access_expression 309 | 310 | access_expression := atom_expression | dot_expression | item_expression | call 311 | 312 | dot_expression := access_expression `.` identifier 313 | 314 | item_expression := access_expression `[` expression `]` 315 | 316 | call := access_expression `(` expression_list `)` 317 | 318 | atom_expression := 319 | function 320 | | class 321 | | tuple 322 | | parenthesized 323 | | variable 324 | | identifier 325 | | literal 326 | 327 | tuple := `(` ( expression `,` )+ [ expression ] `)` 328 | 329 | parenthesized := `(` expression `)` 330 | 331 | literal := 332 | string_literal 333 | | integer_literal 334 | | float_literal 335 | | `true` | `false` 336 | | `nothing` 337 | ``` 338 | 339 | ### Item access 340 | 341 | ### Dot access 342 | 343 | ### Calling 344 | 345 | #### Keyword arguments 346 | 347 | ### Variables 348 | 349 | ### Names 350 | 351 | ### Literals 352 | 353 | 354 | 355 | 356 | ## Compound expressions 357 | 358 | ```grammar 359 | compound_expression := logic_expression 360 | 361 | logic_expression := 362 | not_expression ( `and` not_expression )* 363 | | not_expression ( `or` not_expression )* 364 | 365 | not_expression := 366 | is_expression 367 | | `not` is_expression 368 | 369 | is_expression := 370 | union_expression 371 | | union_expression `is` union_expression 372 | 373 | union_expression := 374 | add_expression 375 | | union_expression `|` add_expression 376 | 377 | add_expression := 378 | mul_expression 379 | | add_expression `+` mul_expression 380 | | add_expression `-` mul_expression 381 | 382 | mul_expression := 383 | simple_expression 384 | | mul_expression `*` simple_expression 385 | | mul_expression `/` simple_expression 386 | | mul_expression `%` simple_expression 387 | ``` 388 | 389 | Note that the logical operators `and` and `or` are not expressed recursively. 390 | You can't combine these operators without making precedence explicit through parentheses. 391 | 392 | ### Addition 393 | 394 | The `+` operator is used for addition, string concatenation and taking the intersection of types: 395 | 396 | + -> 397 | + -> 398 | + -> 399 | + -> 400 | 401 | + -> 402 | 403 | + -> 404 | 405 | ### Subtraction 406 | 407 | ### Multiplication 408 | 409 | ### Division 410 | 411 | ### Modulo 412 | 413 | ### Union 414 | 415 | 416 | 417 | 418 | 419 | ## Functions 420 | 421 | ```grammar 422 | function := `function` `(` [ argument_list ] `)` [ `->` type ] block 423 | argument_list := argument ( ',' argument )* 424 | argument := [ type ] variable [ `=` expression ] 425 | ``` 426 | 427 | 428 | 429 | 430 | 431 | 432 | ## Classes 433 | 434 | ```grammar 435 | class := `class` `{` [ class_items ] `}` 436 | class_items := class_item ( newline class_item )* 437 | class_item := property | method 438 | ``` 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | Core semantics 447 | ============== 448 | 449 | ## Scoping 450 | 451 | ## Type system 452 | 453 | ## Intrinsics 454 | 455 | ## Object-oriented programming 456 | 457 | ## Modules and importing 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | Execution model 466 | =============== 467 | 468 | ## Fibers 469 | 470 | ## Memory management 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | Extending/embedding 479 | =================== 480 | 481 | ## Embedding burn in your application 482 | 483 | Rust's ownership and lifetime semantics make extending and embedding burn very easy. Refcounting and 484 | garbage-collecting happens automatically. The VM is inherently unsafe, but this is easily dealt with: 485 | 486 | * No `Gc` or `Rc` pointers should outlive the virtual machine that created them. 487 | Any `Gc` pointers still alive will segfault when used. 488 | `Rc` pointers will still point to live data, but any `Gc` pointer contained within them will be invalid. 489 | * No `Gc` or `Rc` pointers should be stored outside the VM. 490 | Doing so is likely to wrongly get their contents garbage-collected. 491 | Consider them valid only until the next garbage collection. 492 | 493 |
494 | Externally storable variants of these pointers are planned. 495 |
496 | -------------------------------------------------------------------------------- /src/libburn/parse/lexer.rs: -------------------------------------------------------------------------------- 1 | use std::str::utf8_char_width; 2 | use parse::token; 3 | 4 | pub struct Lexer<'src> { 5 | source: &'src str, 6 | offset: uint, 7 | } 8 | 9 | impl<'src> Lexer<'src> { 10 | 11 | pub fn new( source: &'src str ) -> Lexer<'src> { 12 | Lexer { 13 | source: source, 14 | offset: 0, 15 | } 16 | } 17 | 18 | // todo! make offset part of token 19 | pub fn read( &mut self ) -> (token::Token<'src>, uint) { 20 | loop { 21 | match self.peek_char( self.offset ) { 22 | None => { 23 | return (token::Eof, self.offset); 24 | }, 25 | Some( ' ' ) | Some( '\t' ) => { 26 | self.offset += 1; 27 | }, 28 | Some( '/' ) if self.peek_char( self.offset + 1 ) == Some( '*' ) => { 29 | self.offset += 2; 30 | 31 | let mut stars = 1; 32 | while self.peek_char( self.offset ) == Some( '*' ) { 33 | self.offset += 1; 34 | stars += 1; 35 | } 36 | 37 | let mut found_stars = 0; 38 | loop { 39 | match self.peek_char( self.offset ) { 40 | Some( '*' ) => { 41 | found_stars += 1; 42 | self.offset += 1; 43 | } 44 | Some( '/' ) if found_stars == stars => { 45 | self.offset += 1; 46 | break; 47 | } 48 | Some( _ ) => { 49 | found_stars = 0; 50 | self.offset += 1; 51 | } 52 | None => { 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | Some( '/' ) if self.peek_char( self.offset + 1 ) == Some( '/' ) => { 59 | self.offset += 2; 60 | 61 | loop { 62 | match self.peek_char( self.offset ) { 63 | Some( '\n' ) => { 64 | self.offset += 1; 65 | break; 66 | } 67 | Some( _ ) => { 68 | self.offset += 1; 69 | } 70 | None => { 71 | break; 72 | } 73 | } 74 | } 75 | } 76 | Some( _ ) => { 77 | let offset = self.offset; 78 | let (token, length) = self.match_token(); 79 | self.offset += length; 80 | return (token, offset); 81 | } 82 | } 83 | } 84 | } 85 | 86 | fn peek_char( &self, offset: uint ) -> Option { 87 | if offset < self.source.len() { 88 | Some( self.source[ offset ] as char ) 89 | } else { 90 | None 91 | } 92 | } 93 | 94 | fn match_token( &self ) -> (token::Token<'src>, uint) { 95 | 96 | match self.source[ self.offset ] as char { 97 | 98 | '\n' => (token::Newline, 1), 99 | 100 | // symbols 101 | '{' => (token::LeftCurlyBracket, 1), 102 | '}' => (token::RightCurlyBracket, 1), 103 | '[' => (token::LeftSquareBracket, 1), 104 | ']' => (token::RightSquareBracket, 1), 105 | '(' => (token::LeftParenthesis, 1), 106 | ')' => (token::RightParenthesis, 1), 107 | '.' => (token::Dot, 1), 108 | ',' => (token::Comma, 1), 109 | '=' => match self.peek_char( self.offset + 1 ) { 110 | Some( '=' ) => (token::EqualsEquals, 2), 111 | _ => (token::Equals, 1), 112 | }, 113 | '<' => match self.peek_char( self.offset + 1 ) { 114 | Some( '=' ) => (token::LeftAngleBracketEquals, 2), 115 | _ => (token::LeftAngleBracket, 1), 116 | }, 117 | '>' => match self.peek_char( self.offset + 1 ) { 118 | Some( '=' ) => (token::RightAngleBracketEquals, 2), 119 | _ => (token::RightAngleBracket, 1), 120 | }, 121 | '!' => match self.peek_char( self.offset + 1 ) { 122 | Some( '=' ) => (token::BangEquals, 2), 123 | _ => (token::Error( "Unexpected `!`." ), 1), 124 | }, 125 | 126 | '+' => match self.peek_char( self.offset + 1 ) { 127 | Some( '=' ) => (token::PlusEquals, 2), 128 | _ => (token::Plus, 1), 129 | }, 130 | '-' => match self.peek_char( self.offset + 1 ) { 131 | Some( '0'..'9' ) => self.match_number_literal(), 132 | Some( '=' ) => (token::DashEquals, 2), 133 | Some( '>' ) => (token::Arrow, 2), 134 | _ => (token::Dash, 1), 135 | }, 136 | '*' => match self.peek_char( self.offset + 1 ) { 137 | Some( '=' ) => (token::AsteriskEquals, 2), 138 | _ => (token::Asterisk, 1), 139 | }, 140 | '/' => match self.peek_char( self.offset + 1 ) { 141 | Some( '=' ) => (token::SlashEquals, 2), 142 | _ => (token::Slash, 1), 143 | }, 144 | '%' => match self.peek_char( self.offset + 1 ) { 145 | Some( '=' ) => (token::PercentEquals, 2), 146 | _ => (token::Percent, 1), 147 | }, 148 | '|' => (token::VerticalBar, 1), 149 | 150 | // identifier 151 | 'a'..'z' | 'A'..'Z' | '_' | ':' => { 152 | 153 | let mut length = 1; 154 | 155 | loop { 156 | match self.peek_char( self.offset + length ) { 157 | 158 | Some( 'a'..'z' ) 159 | | Some( 'A'..'Z' ) 160 | | Some( '0'..'9' ) 161 | | Some( '_' ) 162 | | Some( ':' ) 163 | => { length += 1; } 164 | 165 | _ => { break; } 166 | } 167 | } 168 | 169 | let sub = self.source.slice( self.offset, self.offset + length ); 170 | 171 | let value = match sub { 172 | "and" => token::And, 173 | "catch" => token::Catch, 174 | "class" => token::Class, 175 | "else" => token::Else, 176 | "false" => token::False, 177 | "finally" => token::Finally, 178 | "for" => token::For, 179 | "function" => token::Function, 180 | "if" => token::If, 181 | "in" => token::In, 182 | "is" => token::Is, 183 | "let" => token::Let, 184 | "new" => token::New, 185 | "not" => token::Not, 186 | "nothing" => token::Nothing, 187 | "or" => token::Or, 188 | "print" => token::Print, 189 | "return" => token::Return, 190 | "this" => token::This, 191 | "throw" => token::Throw, 192 | "true" => token::True, 193 | "try" => token::Try, 194 | "while" => token::While, 195 | "use" => token::Use, 196 | _ => token::Identifier( sub ), 197 | }; 198 | 199 | (value, length) 200 | } 201 | 202 | // variable 203 | '$' => { 204 | 205 | let mut length = 0; 206 | 207 | loop { 208 | match self.peek_char( self.offset + 1 + length ) { 209 | Some( 'a'..'z' ) 210 | | Some( 'A'..'Z' ) 211 | | Some( '0'..'9' ) 212 | | Some( '_' ) 213 | | Some( ':' ) => { length += 1; }, 214 | _ => { break; } 215 | } 216 | } 217 | 218 | if length == 0 { 219 | (token::Error( "Unexpected `$`." ), 0) 220 | } else { 221 | let sub = self.source.slice( self.offset + 1, self.offset + 1 + length ); 222 | (token::Variable( sub ), 1 + length) 223 | } 224 | } 225 | 226 | // string literals 227 | '"' => { 228 | let mut length = 1; 229 | loop { 230 | match self.peek_char( self.offset + length ) { 231 | Some( '\\' ) => { 232 | length += 2; 233 | } 234 | Some( '"' ) => { 235 | length += 1; 236 | break; 237 | } 238 | Some( _ ) => { 239 | length += utf8_char_width( self.source[ self.offset + length ] ); 240 | } 241 | None => { 242 | return (token::Error( "Unterminated string literal." ), 0); 243 | } 244 | } 245 | } 246 | let sub = self.source.slice( self.offset, self.offset + length ); 247 | (token::String( sub ), length) 248 | } 249 | 250 | '0'..'9' => self.match_number_literal(), 251 | 252 | // invalid 253 | _ => (token::Error( "Unexpected character." ), 0) 254 | } 255 | } 256 | 257 | fn match_number_literal( &self ) -> (token::Token<'src>, uint) { 258 | 259 | let mut l = 0; 260 | let mut float = false; 261 | 262 | 263 | match self.peek_char( self.offset ) { 264 | Some( '-' ) => { 265 | l += 1; 266 | }, 267 | _ => {}, 268 | } 269 | 270 | match self.peek_char( self.offset + l ) { 271 | Some( '0' ) => { 272 | l += 1; 273 | match self.peek_char( self.offset + l ) { 274 | Some( '0'..'9' ) => { 275 | return (token::Error( "Invalid number literal." ), 0); 276 | }, 277 | _ => {} 278 | } 279 | }, 280 | Some( '1'..'9' ) => l += 1, 281 | _ => assert!( false ) 282 | } 283 | 284 | loop { 285 | match self.peek_char( self.offset + l ) { 286 | Some( '0'..'9' ) => l += 1, 287 | Some( '.' ) if ! float => { 288 | match self.peek_char( self.offset + l + 1 ) { 289 | Some( '0'..'9' ) => l += 2, 290 | _ => break, 291 | } 292 | float = true; 293 | } 294 | _ => break 295 | } 296 | } 297 | 298 | let sub = self.source.slice( self.offset, self.offset + l ); 299 | 300 | if float { 301 | (token::Float( sub ), l) 302 | } else { 303 | (token::Integer( sub ), l) 304 | } 305 | } 306 | } 307 | 308 | #[cfg(test)] 309 | mod test { 310 | use std::vec::Vec; 311 | use parse::lexer::Lexer; 312 | use parse::token; 313 | 314 | fn lex<'src>( source: &'src str ) -> Vec> { 315 | let mut lexer = Lexer::new( source ); 316 | let mut tokens = Vec::new(); 317 | loop { 318 | let token = lexer.read(); 319 | 320 | if token == token::Eof { 321 | break; 322 | } 323 | 324 | tokens.push( token ); 325 | 326 | match token { 327 | token::Error(..) => break, 328 | _ => {}, 329 | } 330 | } 331 | tokens 332 | } 333 | 334 | #[test] 335 | fn test_edge_cases() { 336 | assert!( lex( "" ) == vec!() ); 337 | } 338 | 339 | #[test] 340 | fn test_newline() { 341 | assert!( lex( "\n" ) == vec!( token::Newline ) ); 342 | } 343 | 344 | #[test] 345 | fn test_whitespace() { 346 | assert!( lex( " " ) == vec!() ); 347 | assert!( lex( "\t" ) == vec!() ); 348 | } 349 | 350 | #[test] 351 | fn test_comments() { 352 | assert!( lex( "// foo" ) == vec!() ); 353 | assert!( lex( "// foo\n" ) == vec!() ); 354 | assert!( lex( "//\nfoo" ) == vec!( token::Identifier( "foo" ) ) ); 355 | assert!( lex( "/* foo */" ) == vec!() ); 356 | assert!( lex( "/* foo\nbar */" ) == vec!() ); 357 | assert!( lex( "/*" ) == vec!() ); 358 | assert!( lex( "/* foo */ bar" ) == vec!( token::Identifier( "bar" ) ) ); 359 | assert!( lex( "/*** foo ***/ bar" ) == vec!( token::Identifier( "bar" ) ) ); 360 | assert!( lex( "/*** foo */ bar" ) == vec!() ); 361 | assert!( lex( "/* foo ***/ bar" ) == vec!() ); 362 | } 363 | 364 | #[test] 365 | fn test_symbols() { 366 | assert!( lex( "{" ) == vec!( token::LeftCurlyBracket ) ); 367 | assert!( lex( "}" ) == vec!( token::RightCurlyBracket ) ); 368 | assert!( lex( "[" ) == vec!( token::LeftSquareBracket ) ); 369 | assert!( lex( "]" ) == vec!( token::RightSquareBracket ) ); 370 | assert!( lex( "(" ) == vec!( token::LeftParenthesis ) ); 371 | assert!( lex( ")" ) == vec!( token::RightParenthesis ) ); 372 | assert!( lex( "<" ) == vec!( token::LeftAngleBracket ) ); 373 | assert!( lex( ">" ) == vec!( token::RightAngleBracket ) ); 374 | assert!( lex( "." ) == vec!( token::Dot ) ); 375 | assert!( lex( "," ) == vec!( token::Comma ) ); 376 | assert!( lex( "=" ) == vec!( token::Equals ) ); 377 | assert!( lex( "+" ) == vec!( token::Plus ) ); 378 | assert!( lex( "-" ) == vec!( token::Dash ) ); 379 | assert!( lex( "*" ) == vec!( token::Asterisk ) ); 380 | assert!( lex( "/" ) == vec!( token::Slash ) ); 381 | assert!( lex( "%" ) == vec!( token::Percent ) ); 382 | assert!( lex( "|" ) == vec!( token::VerticalBar ) ); 383 | assert!( lex( "<=" ) == vec!( token::LeftAngleBracketEquals ) ); 384 | assert!( lex( ">=" ) == vec!( token::RightAngleBracketEquals ) ); 385 | assert!( lex( "+=" ) == vec!( token::PlusEquals ) ); 386 | assert!( lex( "-=" ) == vec!( token::DashEquals ) ); 387 | assert!( lex( "*=" ) == vec!( token::AsteriskEquals ) ); 388 | assert!( lex( "/=" ) == vec!( token::SlashEquals ) ); 389 | assert!( lex( "%=" ) == vec!( token::PercentEquals ) ); 390 | assert!( lex( "==" ) == vec!( token::EqualsEquals ) ); 391 | assert!( lex( "!=" ) == vec!( token::BangEquals ) ); 392 | assert!( lex( "->" ) == vec!( token::Arrow ) ); 393 | 394 | assert!( lex( "!" ) == vec!( token::Error( "Unexpected `!`." ) ) ); 395 | } 396 | 397 | #[test] 398 | fn test_keywords() { 399 | assert!( lex( "and" ) == vec!( token::And ) ); 400 | assert!( lex( "catch" ) == vec!( token::Catch ) ); 401 | assert!( lex( "class" ) == vec!( token::Class ) ); 402 | assert!( lex( "else" ) == vec!( token::Else ) ); 403 | assert!( lex( "false" ) == vec!( token::False ) ); 404 | assert!( lex( "finally" ) == vec!( token::Finally ) ); 405 | assert!( lex( "for" ) == vec!( token::For ) ); 406 | assert!( lex( "function" ) == vec!( token::Function ) ); 407 | assert!( lex( "if" ) == vec!( token::If ) ); 408 | assert!( lex( "in" ) == vec!( token::In ) ); 409 | assert!( lex( "is" ) == vec!( token::Is ) ); 410 | assert!( lex( "let" ) == vec!( token::Let ) ); 411 | assert!( lex( "new" ) == vec!( token::New ) ); 412 | assert!( lex( "not" ) == vec!( token::Not ) ); 413 | assert!( lex( "nothing" ) == vec!( token::Nothing ) ); 414 | assert!( lex( "or" ) == vec!( token::Or ) ); 415 | assert!( lex( "return" ) == vec!( token::Return ) ); 416 | assert!( lex( "this" ) == vec!( token::This ) ); 417 | assert!( lex( "throw" ) == vec!( token::Throw ) ); 418 | assert!( lex( "true" ) == vec!( token::True ) ); 419 | assert!( lex( "try" ) == vec!( token::Try ) ); 420 | assert!( lex( "while" ) == vec!( token::While ) ); 421 | assert!( lex( "use" ) == vec!( token::Use ) ); 422 | } 423 | 424 | #[test] 425 | fn test_identifiers() { 426 | assert!( lex( "foo" ) == vec!( token::Identifier( "foo" ) ) ); 427 | assert!( lex( "Foo" ) == vec!( token::Identifier( "Foo" ) ) ); 428 | assert!( lex( "FOO" ) == vec!( token::Identifier( "FOO" ) ) ); 429 | assert!( lex( "foo_bar" ) == vec!( token::Identifier( "foo_bar" ) ) ); 430 | assert!( lex( "_foo" ) == vec!( token::Identifier( "_foo" ) ) ); 431 | assert!( lex( "foo_" ) == vec!( token::Identifier( "foo_" ) ) ); 432 | assert!( lex( "foo:bar" ) == vec!( token::Identifier( "foo:bar" ) ) ); 433 | assert!( lex( ":foo" ) == vec!( token::Identifier( ":foo" ) ) ); 434 | assert!( lex( "foo:" ) == vec!( token::Identifier( "foo:" ) ) ); 435 | assert!( lex( "foo123" ) == vec!( token::Identifier( "foo123" ) ) ); 436 | assert!( lex( "123foo" ) == vec!( token::Integer( "123" ), token::Identifier( "foo" ) ) ); 437 | } 438 | 439 | #[test] 440 | fn test_variables() { 441 | assert!( lex( "$" ) == vec!( token::Error( "Unexpected `$`." ) ) ); 442 | assert!( lex( "$foo" ) == vec!( token::Variable( "foo" ) ) ); 443 | assert!( lex( "$foo_bar" ) == vec!( token::Variable( "foo_bar" ) ) ); 444 | assert!( lex( "$_foo" ) == vec!( token::Variable( "_foo" ) ) ); 445 | assert!( lex( "$foo_" ) == vec!( token::Variable( "foo_" ) ) ); 446 | assert!( lex( "$foo:bar" ) == vec!( token::Variable( "foo:bar" ) ) ); 447 | assert!( lex( "$:foo" ) == vec!( token::Variable( ":foo" ) ) ); 448 | assert!( lex( "$foo:" ) == vec!( token::Variable( "foo:" ) ) ); 449 | assert!( lex( "$foo123" ) == vec!( token::Variable( "foo123" ) ) ); 450 | assert!( lex( "$123foo" ) == vec!( token::Variable( "123foo" ) ) ); 451 | } 452 | 453 | #[test] 454 | fn test_literals() { 455 | assert!( lex( "0" ) == vec!( token::Integer( "0" ) ) ); 456 | assert!( lex( "-0" ) == vec!( token::Integer( "-0" ) ) ); 457 | assert!( lex( "3" ) == vec!( token::Integer( "3" ) ) ); 458 | assert!( lex( "-10" ) == vec!( token::Integer( "-10" ) ) ); 459 | assert!( lex( "03" ) == vec!( token::Error( "Invalid number literal." ) ) ); 460 | 461 | assert!( lex( "0.1" ) == vec!( token::Float( "0.1" ) ) ); 462 | assert!( lex( "1.1" ) == vec!( token::Float( "1.1" ) ) ); 463 | assert!( lex( "12.34" ) == vec!( token::Float( "12.34" ) ) ); 464 | assert!( lex( "1." ) == vec!( token::Integer( "1" ), token::Dot ) ); 465 | assert!( lex( ".1" ) == vec!( token::Dot, token::Integer( "1" ) ) ); 466 | 467 | assert!( lex( "\"\"" ) == vec!( token::String( "\"\"" ) ) ); 468 | assert!( lex( "\"test\"" ) == vec!( token::String( "\"test\"" ) ) ); 469 | assert!( lex( "\"" ) == vec!( token::Error( "Unterminated string literal." ) ) ); 470 | } 471 | 472 | #[test] 473 | fn test_invalid() { 474 | assert!( lex( "#" ) == vec!( token::Error( "Unexpected character." ) ) ); 475 | assert!( lex( "僯" ) == vec!( token::Error( "Unexpected character." ) ) ); 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /src/libburn/vm/analysis/resolution.rs: -------------------------------------------------------------------------------- 1 | use mem::raw::Raw; 2 | use mem::rc::Rc; 3 | use lang::origin::Origin; 4 | use lang::identifier::Identifier; 5 | use parse::node; 6 | use vm::error::{AnalysisError, Error}; 7 | use vm::analysis::annotation; 8 | use vm::repl; 9 | 10 | struct Scope { 11 | frame: Raw, 12 | declared_variables: Vec>, 13 | used: Vec>, 14 | } 15 | 16 | pub struct AnalyzeResolution<'o> { 17 | origin: &'o Rc>, 18 | frames: Vec>, 19 | scopes: Vec, 20 | time: annotation::Time, 21 | pub errors: Vec>, 22 | } 23 | 24 | impl<'o> AnalyzeResolution<'o> { 25 | 26 | pub fn new<'l>( origin: &'l Rc> ) -> AnalyzeResolution<'l> { 27 | AnalyzeResolution { 28 | origin: origin, 29 | frames: Vec::new(), 30 | scopes: Vec::new(), 31 | time: 0, 32 | errors: Vec::new(), 33 | } 34 | } 35 | 36 | pub fn analyze_root( &mut self, root: &mut node::Root, repl_state: &mut Option<&mut repl::State> ) { 37 | 38 | self.push_frame( &mut root.frame ); 39 | self.push_scope(); 40 | 41 | repl_state.as_ref().map( |repl_state| { 42 | // put repl_state vars into the root scope 43 | for &name in repl_state.variables.keys() { 44 | self.declare_variable( name ); 45 | } 46 | } ); 47 | 48 | self.analyze_block( &mut root.statements ); 49 | 50 | // put any new vars into repl_state 51 | repl_state.as_mut().map( |repl_state| { 52 | let declared_variables = self.get_current_scope().declared_variables.iter(); 53 | let mut new_variables = declared_variables.skip( repl_state.variables.len() ); 54 | for variable in new_variables { 55 | repl_state.declare_variable( variable.name ); 56 | } 57 | } ); 58 | 59 | self.pop_scope(); 60 | self.pop_frame(); 61 | } 62 | 63 | fn analyze_block( &mut self, block: &mut Vec> ) { 64 | for statement in block.mut_iter() { 65 | self.analyze_statement( *statement ); 66 | } 67 | } 68 | 69 | fn analyze_statement( &mut self, statement: &mut node::Statement ) { 70 | match *statement { 71 | 72 | node::Use { 73 | path: _, 74 | annotation: ref mut annotation, 75 | } => { 76 | // todo! check for duplicate use 77 | self.get_current_scope().used.push( Raw::new( annotation ) ); 78 | } 79 | 80 | node::ExpressionStatement { expression: ref mut expression } 81 | | node::Print { expression: ref mut expression } 82 | => { 83 | self.analyze_expression( *expression ); 84 | } 85 | 86 | node::Return { expression: ref mut optional_expression } 87 | => { 88 | match *optional_expression { 89 | Some( ref mut expression ) => { 90 | self.analyze_expression( *expression ); 91 | } 92 | None => {} 93 | } 94 | } 95 | 96 | node::Let { 97 | variable_offset: variable_offset, 98 | variable_name: name, 99 | annotation: ref mut annotation, 100 | default: ref mut default, 101 | } => { 102 | 103 | let is_duplicate = self.get_current_scope().declared_variables.iter() 104 | .find( |v| { v.name == name } ).is_some(); 105 | 106 | if is_duplicate { 107 | self.error( 108 | format!( "Duplicate declaration of ${}", name ), 109 | variable_offset 110 | ); 111 | } 112 | 113 | *annotation = self.declare_variable( name ); 114 | 115 | match *default { 116 | Some( ref mut expression ) => { 117 | self.analyze_expression( *expression ); 118 | self.write_variable( *annotation ); 119 | } 120 | None => {} 121 | }; 122 | } 123 | 124 | node::Assignment { 125 | lvalue: ref mut lvalue, 126 | rvalue: ref mut rvalue, 127 | } => { 128 | self.analyze_lvalue_preparation( *lvalue ); 129 | self.analyze_expression( *rvalue ); 130 | self.analyze_lvalue_write( *lvalue ); 131 | } 132 | 133 | node::If { 134 | test: ref mut if_test, 135 | block: ref mut if_block, 136 | else_if_clauses: ref mut else_if_clauses, 137 | else_clause: ref mut else_clause, 138 | } => { 139 | 140 | self.analyze_expression( *if_test ); 141 | self.push_scope(); 142 | self.analyze_block( if_block ); 143 | self.pop_scope(); 144 | 145 | for else_if_clause in else_if_clauses.mut_iter() { 146 | self.analyze_expression( else_if_clause.test ); 147 | self.push_scope(); 148 | self.analyze_block( &mut else_if_clause.block ); 149 | self.pop_scope(); 150 | } 151 | 152 | match *else_clause { 153 | Some( ref mut else_clause ) => { 154 | self.push_scope(); 155 | self.analyze_block( &mut else_clause.block ); 156 | self.pop_scope(); 157 | } 158 | None => {} 159 | } 160 | } 161 | 162 | node::While { 163 | test: ref mut while_test, 164 | block: ref mut while_block, 165 | else_clause: ref mut else_clause, 166 | } => { 167 | 168 | self.analyze_expression( *while_test ); 169 | self.push_scope(); 170 | let start = self.tick(); 171 | self.analyze_block( while_block ); 172 | let end = self.tick(); 173 | self.repeat_variable_usages( start, end ); 174 | self.pop_scope(); 175 | 176 | match *else_clause { 177 | Some( ref mut else_clause ) => { 178 | self.push_scope(); 179 | self.analyze_block( &mut else_clause.block ); 180 | self.pop_scope(); 181 | } 182 | None => {} 183 | } 184 | } 185 | 186 | node::Try { 187 | block: ref mut try_block, 188 | catch_clauses: ref mut catch_clauses, 189 | else_clause: ref mut else_clause, 190 | finally_clause: ref mut finally_clause, 191 | } => { 192 | 193 | self.push_scope(); 194 | self.analyze_block( try_block ); 195 | self.pop_scope(); 196 | 197 | for catch_clause in catch_clauses.mut_iter() { 198 | 199 | match catch_clause.type_ { 200 | Some( ref mut expression ) => { 201 | self.analyze_expression( *expression ); 202 | } 203 | None => {} 204 | } 205 | 206 | self.push_scope(); 207 | 208 | catch_clause.variable = self.declare_variable( catch_clause.variable_name ); 209 | self.analyze_block( &mut catch_clause.block ); 210 | 211 | self.pop_scope(); 212 | } 213 | 214 | match *else_clause { 215 | Some( ref mut else_clause ) => { 216 | self.push_scope(); 217 | self.analyze_block( &mut else_clause.block ); 218 | self.pop_scope(); 219 | } 220 | None => {} 221 | } 222 | 223 | match *finally_clause { 224 | Some( ref mut finally_clause ) => { 225 | self.push_scope(); 226 | self.analyze_block( &mut finally_clause.block ); 227 | self.pop_scope(); 228 | } 229 | None => {} 230 | } 231 | } 232 | 233 | _ => { fail!(); } 234 | } 235 | } 236 | 237 | fn analyze_expression( &mut self, expression: &mut node::Expression ) { 238 | 239 | let expression_ptr = Raw::new( expression ); 240 | 241 | match *expression { 242 | 243 | node::Nothing 244 | | node::Boolean {..} 245 | | node::Integer {..} 246 | | node::Float {..} 247 | | node::String {..} 248 | => {} 249 | 250 | node::Variable { 251 | name: name, 252 | annotation: ref mut annotation, 253 | source_offset: source_offset, 254 | } => { 255 | match self.find_variable( name ) { 256 | Ok( variable ) => { 257 | *annotation = variable; 258 | self.read_variable( variable ); 259 | } 260 | Err(..) => { 261 | self.error( 262 | format!( "Variable not found: ${}", name ), 263 | source_offset 264 | ); 265 | } 266 | }; 267 | } 268 | 269 | node::Name { 270 | identifier: identifier, 271 | annotation: ref mut annotation, 272 | } => { 273 | annotation.resolution = self.find_name( identifier ); 274 | } 275 | 276 | node::ItemAccess { 277 | expression: ref mut expression, 278 | key_expression: ref mut key_expression, 279 | } => { 280 | self.analyze_expression( *expression ); 281 | self.analyze_expression( *key_expression ); 282 | } 283 | 284 | node::DotAccess { 285 | expression: ref mut expression, 286 | name: _, 287 | } => { 288 | self.analyze_expression( *expression ); 289 | } 290 | 291 | node::Call { 292 | expression: ref mut expression, 293 | arguments: ref mut arguments, 294 | } => { 295 | self.analyze_expression( *expression ); 296 | for argument in arguments.mut_iter() { 297 | self.analyze_expression( *argument ); 298 | } 299 | } 300 | 301 | node::Multiplication { left: ref mut left, right: ref mut right } 302 | | node::Division { left: ref mut left, right: ref mut right } 303 | | node::Addition { left: ref mut left, right: ref mut right } 304 | | node::Subtraction { left: ref mut left, right: ref mut right } 305 | | node::Union { left: ref mut left, right: ref mut right } 306 | | node::Is { left: ref mut left, right: ref mut right } 307 | | node::Eq { left: ref mut left, right: ref mut right } 308 | | node::Neq { left: ref mut left, right: ref mut right } 309 | | node::Lt { left: ref mut left, right: ref mut right } 310 | | node::Gt { left: ref mut left, right: ref mut right } 311 | | node::LtEq { left: ref mut left, right: ref mut right } 312 | | node::GtEq { left: ref mut left, right: ref mut right } 313 | | node::And { left: ref mut left, right: ref mut right } 314 | | node::Or { left: ref mut left, right: ref mut right } 315 | => { 316 | self.analyze_expression( *left ); 317 | self.analyze_expression( *right ); 318 | } 319 | 320 | node::Not { 321 | expression: ref mut expression, 322 | } => { 323 | self.analyze_expression( *expression ); 324 | } 325 | 326 | node::Function { 327 | parameters: ref mut parameters, 328 | frame: ref mut frame, 329 | block: ref mut block, 330 | } => { 331 | 332 | frame.get_closure().created_at = self.tick(); 333 | 334 | self.get_current_frame().functions.push( expression_ptr ); 335 | 336 | self.push_frame( frame ); 337 | 338 | for parameter in parameters.mut_iter() { 339 | match parameter.type_ { 340 | Some( ref mut expression ) => { 341 | self.analyze_expression( *expression ); 342 | } 343 | None => {}, 344 | }; 345 | match parameter.default { 346 | Some( ref mut expression ) => { 347 | self.analyze_expression( *expression ); 348 | } 349 | None => {}, 350 | }; 351 | } 352 | 353 | self.push_scope(); 354 | for parameter in parameters.mut_iter() { 355 | parameter.variable = self.declare_variable( parameter.variable_name ); 356 | } 357 | self.analyze_block( block ); 358 | self.pop_scope(); 359 | self.pop_frame(); 360 | } 361 | } 362 | } 363 | 364 | fn analyze_lvalue_preparation( &mut self, lvalue: &mut node::Lvalue ) { 365 | match *lvalue { 366 | 367 | node::VariableLvalue { 368 | name: name, 369 | annotation: ref mut annotation, 370 | source_offset: source_offset, 371 | } => { 372 | match self.find_variable( name ) { 373 | Ok( variable ) => { 374 | *annotation = variable; 375 | } 376 | Err(..) => { 377 | self.error( 378 | format!( "Variable not found: ${}.", name ), 379 | source_offset 380 | ); 381 | } 382 | } 383 | } 384 | 385 | node::DotAccessLvalue { 386 | expression: ref mut expression, 387 | name: _, 388 | } => { 389 | self.analyze_expression( *expression ); 390 | } 391 | } 392 | } 393 | 394 | fn analyze_lvalue_write( &mut self, lvalue: &mut node::Lvalue ) { 395 | match *lvalue { 396 | 397 | node::VariableLvalue { 398 | name: _, 399 | annotation: annotation, 400 | source_offset: _, 401 | } => { 402 | // the variable might not have been found 403 | if ! annotation.is_null() { 404 | self.write_variable( annotation ); 405 | } 406 | } 407 | 408 | node::DotAccessLvalue {..} => {} 409 | } 410 | } 411 | 412 | // 413 | // Helpers 414 | // 415 | 416 | fn error( &mut self, message: String, source_offset: uint ) { 417 | self.errors.push( box AnalysisError { 418 | message: message, 419 | origin: self.origin.clone(), 420 | source_offset: source_offset, 421 | } as Box ); 422 | } 423 | 424 | fn tick( &mut self ) -> annotation::Time { 425 | self.time += 1; 426 | self.time 427 | } 428 | 429 | fn push_frame( &mut self, frame: &mut annotation::Frame ) { 430 | self.frames.push( Raw::new( frame ) ); 431 | } 432 | 433 | fn pop_frame( &mut self ) { 434 | self.frames.pop(); 435 | } 436 | 437 | fn push_scope( &mut self ) { 438 | let frame_ptr = self.get_current_frame(); 439 | self.scopes.push( Scope { 440 | frame: frame_ptr, 441 | declared_variables: Vec::new(), 442 | used: Vec::new(), 443 | } ); 444 | } 445 | 446 | fn pop_scope( &mut self ) { 447 | self.scopes.pop(); 448 | } 449 | 450 | fn get_current_scope<'l>( &'l mut self ) -> &'l mut Scope { 451 | self.scopes.mut_last().unwrap() 452 | } 453 | 454 | fn get_current_frame<'l>( &'l self ) -> Raw { 455 | *self.frames.last().unwrap() 456 | } 457 | 458 | fn declare_variable( &mut self, name: Identifier ) -> Raw { 459 | 460 | let mut variable = box annotation::Variable::new( name ); 461 | let ptr = Raw::new( variable ); 462 | 463 | variable.declared_in = self.get_current_frame(); 464 | self.get_current_scope().declared_variables.push( ptr ); 465 | self.get_current_frame().declared_variables.push( variable ); 466 | 467 | ptr 468 | } 469 | 470 | fn find_variable( &mut self, name: Identifier ) -> Result,()> { 471 | 472 | for scope in self.scopes.iter().rev() { 473 | for &variable in scope.declared_variables.iter() { 474 | if variable.name == name { 475 | return Ok( variable ); 476 | } 477 | } 478 | } 479 | 480 | Err( () ) 481 | } 482 | 483 | fn find_name( &mut self, name: Identifier ) -> annotation::NameResolution { 484 | 485 | for scope in self.scopes.iter().rev() { 486 | for &use_ in scope.used.iter() { 487 | if use_.name == name { 488 | return annotation::Use( use_ ); 489 | } 490 | } 491 | } 492 | 493 | annotation::Implicit 494 | } 495 | 496 | 497 | fn read_variable( &mut self, mut variable: Raw ) { 498 | if self.get_current_frame() == variable.declared_in { 499 | variable.reads.push( annotation::ReadVariable { time: self.tick() } ); 500 | } else { 501 | self.bind_variable( variable, false ); 502 | } 503 | } 504 | 505 | fn write_variable( &mut self, mut variable: Raw ) { 506 | if self.get_current_frame() == variable.declared_in { 507 | variable.writes.push( annotation::WriteVariable { time: self.tick() } ); 508 | } else { 509 | self.bind_variable( variable, true ); 510 | } 511 | } 512 | 513 | fn bind_variable( &mut self, mut variable: Raw, mutable: bool ) { 514 | 515 | let mut time = 0; 516 | 517 | 'frame_loop: for &mut frame in self.frames.iter().rev() { 518 | 519 | if frame == variable.declared_in { 520 | break; 521 | } 522 | 523 | time = frame.get_closure().created_at; 524 | 525 | for binding in frame.get_closure().bindings.mut_iter() { 526 | 527 | if binding.variable == variable { 528 | 529 | if ! mutable { 530 | return; 531 | } else { 532 | binding.mutable = true; 533 | continue 'frame_loop; 534 | } 535 | } 536 | } 537 | 538 | frame.get_closure().bindings.push( annotation::Binding { 539 | variable: variable, 540 | mutable: mutable, 541 | storage_index: 0, 542 | } ); 543 | variable.n_binds += 1; 544 | } 545 | 546 | for binding in variable.root_binds.mut_iter() { 547 | if binding.time == time { 548 | binding.mutable = binding.mutable || mutable; 549 | return; 550 | } 551 | } 552 | 553 | variable.root_binds.push( annotation::BindVariable { 554 | time: time, 555 | mutable: mutable, 556 | } ); 557 | variable.n_binds += 1; 558 | } 559 | 560 | fn repeat_variable_usages( &mut self, from: annotation::Time, to: annotation::Time ) { 561 | 562 | let current_frame = self.get_current_frame(); 563 | 564 | for scope in self.scopes.iter().rev() { 565 | 566 | if scope.frame != current_frame { 567 | break; 568 | } 569 | 570 | for &mut variable in scope.declared_variables.iter() { 571 | 572 | let is_read = variable.reads.iter() 573 | .find( |r| { from < r.time && r.time < to } ).is_some(); 574 | 575 | if is_read { 576 | variable.reads.push( annotation::ReadVariable { 577 | time: to, 578 | } ); 579 | } 580 | 581 | let is_written = variable.writes.iter() 582 | .find( |w| { from < w.time && w.time < to } ).is_some(); 583 | 584 | if is_written { 585 | variable.writes.push( annotation::WriteVariable { 586 | time: to, 587 | } ); 588 | } 589 | 590 | let mut is_bound = false; 591 | let mut is_bound_mutable = false; 592 | for bind in variable.root_binds.iter() { 593 | if from < bind.time && bind.time < to { 594 | is_bound = true; 595 | if bind.mutable { 596 | is_bound_mutable = true; 597 | break; 598 | } 599 | } 600 | } 601 | 602 | if is_bound { 603 | variable.root_binds.push( annotation::BindVariable { 604 | time: to, 605 | mutable: is_bound_mutable, 606 | } ); 607 | variable.n_binds += 1; 608 | } 609 | } 610 | } 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /src/libburn/vm/run/cpu.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use mem::rc::Rc; 3 | use lang::value; 4 | use lang::function; 5 | use lang::operations; 6 | use vm::bytecode::opcode; 7 | use vm::virtual_machine::VirtualMachine; 8 | use vm::run::fiber::Fiber; 9 | use vm::run::{frame, flow, rust}; 10 | use vm::run::rust::Operation; 11 | use builtin::burn::{errors, types}; 12 | 13 | pub fn run( vm: &mut VirtualMachine, mut fiber: Box ) { 14 | 15 | 'frame_loop: loop { 16 | 17 | macro_rules! new_frame( 18 | ( $frame:expr ) => {{ 19 | let frame = $frame; 20 | 21 | let is_running = match fiber.flow { 22 | flow::Running => true, 23 | _ => false, 24 | }; 25 | 26 | fiber.push_frame( frame ); 27 | 28 | if is_running { 29 | 30 | fiber.flow_points.push( 31 | flow::PopFrame { data_stack_len: fiber.data_stack.len() } 32 | ); 33 | 34 | } else { 35 | 36 | let suppressed = fiber.replace_flow( flow::Running ); 37 | fiber.suppressed_flows.push( suppressed ); 38 | 39 | fiber.flow_points.push( 40 | flow::PopFrameAndRestoreFlow { data_stack_len: fiber.data_stack.len() } 41 | ); 42 | } 43 | 44 | continue 'frame_loop; 45 | }} 46 | ) 47 | 48 | if fiber.frame.is_rust_operation() { 49 | 50 | match fiber.flow.clone() { // optimize! get rid of this clone 51 | 52 | flow::Running => { 53 | 54 | let result = match fiber.replace_flow( flow::Running ) { 55 | flow::Running => Ok( value::Nothing ), 56 | flow::Returning( v ) => Ok( v ), 57 | flow::Throwing( v ) => Err( v ), 58 | _ => { unreachable!() }, 59 | }; 60 | 61 | match fiber.frame.get_rust_operation().run( vm, result ) { 62 | 63 | rust::Ok( value ) => { 64 | fiber.set_flow( flow::Returning( value ) ); 65 | } 66 | 67 | rust::Throw( value ) => { 68 | fiber.set_flow( flow::Throwing( value ) ); 69 | } 70 | 71 | rust::Burn( frame ) => { 72 | fiber.flow_points.push( flow::PopFrame { data_stack_len: fiber.data_stack.len() } ); 73 | new_frame!( frame ); 74 | } 75 | 76 | _ => { unimplemented!(); } 77 | } 78 | } 79 | 80 | flow::Returning( value ) => { 81 | loop { 82 | match fiber.flow_points.pop().unwrap() { 83 | 84 | flow::PopFrame { data_stack_len: n } => { 85 | fiber.pop_frame(); 86 | fiber.data_stack.truncate( n ); 87 | fiber.push_data( value ); 88 | fiber.set_flow( flow::Running ); 89 | continue 'frame_loop; 90 | } 91 | 92 | flow::PopFrameAndRestoreFlow { data_stack_len: n } => { 93 | fiber.pop_frame(); 94 | fiber.data_stack.truncate( n ); 95 | fiber.push_data( value ); 96 | fiber.restore_flow(); 97 | continue 'frame_loop; 98 | } 99 | 100 | _ => { fail!(); } 101 | } 102 | } 103 | } 104 | 105 | _ => { fail!(); } 106 | } 107 | 108 | } else { // not a rust-type frame 109 | 110 | let opcodes = fiber.frame.get_code().opcodes.as_mut_ptr(); 111 | 112 | 'flow_loop: loop { 113 | match fiber.flow.clone() { // optimize! workaround because of rust#6393 114 | 115 | flow::Running | flow::Catching(..) => { 116 | 117 | 'instruction_loop: loop { 118 | 119 | debug!( { 120 | let instruction = fiber.frame.get_context().instruction; 121 | println!( 122 | "VM: running {}/{} ({})", 123 | instruction, 124 | fiber.frame.get_code().opcodes.len(), 125 | fiber.flow_points.len() 126 | ); 127 | } ) 128 | 129 | macro_rules! throw ( 130 | ( $throwable:expr ) => {{ 131 | fiber.set_flow( flow::Throwing( $throwable ) ); 132 | continue 'flow_loop; 133 | }} 134 | ) 135 | 136 | macro_rules! handle_operation_result ( 137 | ( $operation:expr ) => {{ 138 | match $operation { 139 | rust::Ok( result ) => { fiber.push_data( result ); } 140 | rust::Throw( t ) => { throw!( t ); } 141 | rust::Burn( frame ) => { 142 | new_frame!( frame ); 143 | } 144 | _ => { unimplemented!(); } 145 | }; 146 | }} 147 | ) 148 | 149 | match unsafe { *opcodes.offset( fiber.frame.get_context().instruction as int ) } { 150 | 151 | // Temporary 152 | 153 | opcode::ToString => { 154 | handle_operation_result!( operations::to_string( &fiber.pop_data() ) ); 155 | } 156 | 157 | opcode::Print => { 158 | match fiber.pop_data() { 159 | value::String( s ) => println!( "{}", *s ), 160 | _ => { unreachable!(); } 161 | }; 162 | } 163 | 164 | // VM 165 | 166 | opcode::Nop => {} 167 | 168 | opcode::Fail => { 169 | fail!(); 170 | } 171 | 172 | // Flow 173 | 174 | opcode::PopFlowPoint => { 175 | fiber.flow_points.pop(); 176 | } 177 | 178 | opcode::Jump { instruction: i } => { 179 | fiber.frame.get_context().instruction = i; 180 | continue 'instruction_loop; 181 | } 182 | 183 | opcode::JumpIfPopFalsy { instruction: i } => { 184 | if ! operations::is_truthy( &fiber.pop_data() ) { 185 | fiber.frame.get_context().instruction = i; 186 | continue 'instruction_loop; 187 | } 188 | } 189 | 190 | opcode::FlowJump { n_flow_points: n, instruction: i } => { 191 | fiber.set_flow( flow::Jumping { n_flow_points: n, instruction: i } ); 192 | continue 'flow_loop; 193 | } 194 | 195 | // Functions 196 | 197 | opcode::Call { n_arguments: n_arguments } => { 198 | 199 | let function_offset = fiber.data_stack.len() - n_arguments - 1; 200 | // optimize! could use an unsafe copy here, with an unsafe set_len later 201 | let function = mem::replace( fiber.data_stack.get_mut( function_offset ), value::Nothing ); 202 | 203 | match function { 204 | 205 | value::Function( mut function ) => { 206 | 207 | fiber.frame.get_context().instruction += 1; 208 | 209 | let mut locals = Vec::from_elem( 210 | function.definition.code.n_local_variables, 211 | value::Nothing 212 | ); 213 | let mut shared = Vec::from_elem( 214 | function.definition.code.n_shared_local_variables, 215 | None 216 | ); 217 | 218 | { 219 | let parameters = function.definition.parameters.as_slice(); 220 | assert!( n_arguments == parameters.len() ); 221 | for parameter in parameters.iter().rev() { 222 | match parameter.storage { 223 | function::LocalFunctionParameterStorage( i ) => { 224 | *locals.get_mut( i ) = fiber.pop_data(); 225 | } 226 | function::SharedLocalFunctionParameterStorage( i ) => { 227 | *shared.get_mut( i ) = Some( Rc::new( fiber.pop_data() ) ); 228 | } 229 | }; 230 | } 231 | } 232 | 233 | new_frame!( frame::BurnFunctionFrame { 234 | context: frame::BurnContext::new( locals, shared ), 235 | function: function, 236 | } ); 237 | } 238 | 239 | _ => { unimplemented!(); } 240 | } 241 | } 242 | 243 | opcode::TypeCheckLocal { index: _ } => { 244 | unimplemented!(); 245 | } 246 | 247 | opcode::TypeCheckSharedLocal { index: _ } => { 248 | unimplemented!(); 249 | } 250 | 251 | opcode::Return => { 252 | let value = fiber.pop_data(); 253 | if fiber.frame_stack.len() > 0 { 254 | let flow = flow::Returning( value ); 255 | fiber.set_flow( flow ); 256 | continue 'flow_loop; 257 | } else { 258 | fiber.end_return( value ); 259 | return; 260 | } 261 | } 262 | 263 | opcode::ReturnNothing => { 264 | if fiber.frame_stack.len() > 0 { 265 | fiber.set_flow( flow::Returning( value::Nothing ) ); 266 | continue 'flow_loop; 267 | } else { 268 | fiber.end_return( value::Nothing ); 269 | return; 270 | } 271 | } 272 | 273 | // Try/Catch 274 | 275 | opcode::PushStartCatchFlowPoint { instruction: i } => { 276 | fiber.flow_points.push( flow::StartCatch { instruction: i } ); 277 | } 278 | 279 | opcode::PushStartFinallyFlowPoint { instruction: i } => { 280 | fiber.flow_points.push( flow::StartFinally { instruction: i } ); 281 | } 282 | 283 | opcode::Throw => { 284 | 285 | let throwable = fiber.pop_data(); 286 | 287 | if types::is_throwable( &throwable ) { 288 | throw!( throwable ); 289 | } else { 290 | let message = format!( "{} is not Throwable.", operations::repr( &throwable ) ); 291 | let error = errors::create_type_error( message ); 292 | throw!( error ); 293 | } 294 | } 295 | 296 | opcode::ThrownIs => { 297 | 298 | // we only borrow the throwable, so we have to limit its lifetime 299 | let result = { 300 | let type_ = fiber.pop_data(); 301 | let throwable = match_enum!( fiber.flow to flow::Catching( ref t ) => { t } ); 302 | operations::is( throwable, &type_ ) 303 | }; 304 | 305 | handle_operation_result!( result ); 306 | } 307 | 308 | opcode::CatchLocalOrJump { storage_index: s_i, instruction: i } => { 309 | match fiber.pop_data() { 310 | value::Boolean( true ) => { 311 | let throwable = fiber.replace_flow( flow::Running ).unwrap_throwable(); 312 | *fiber.frame.get_local_variable( s_i ) = throwable; 313 | } 314 | _ => { 315 | fiber.frame.get_context().instruction = i; 316 | continue 'instruction_loop; 317 | } 318 | }; 319 | } 320 | 321 | opcode::CatchSharedLocalOrJump { storage_index: s_i, instruction: i } => { 322 | match fiber.pop_data() { 323 | value::Boolean( true ) => { 324 | let throwable = fiber.replace_flow( flow::Running ).unwrap_throwable(); 325 | *fiber.frame.get_shared_local_variable( s_i ) = Some( Rc::new( throwable ) ); 326 | } 327 | _ => { 328 | fiber.frame.get_context().instruction = i; 329 | continue 'instruction_loop; 330 | } 331 | }; 332 | } 333 | 334 | opcode::CatchLocal { storage_index: s_i } => { 335 | let throwable = fiber.replace_flow( flow::Running ).unwrap_throwable(); 336 | *fiber.frame.get_local_variable( s_i ) = throwable; 337 | } 338 | 339 | opcode::CatchSharedLocal { storage_index: s_i } => { 340 | let throwable = fiber.replace_flow( flow::Running ).unwrap_throwable(); 341 | *fiber.frame.get_shared_local_variable( s_i ) = Some( Rc::new( throwable ) ); 342 | } 343 | 344 | opcode::Rethrow => { 345 | let throwable = match fiber.flow { 346 | flow::Catching( ref t ) => t.clone(), // optimize! workaround because of rust#6393 347 | _ => fail!(), 348 | }; 349 | throw!( throwable ); 350 | } 351 | 352 | opcode::StartFinally => { 353 | fiber.flow_points.pop(); 354 | fiber.suppressed_flows.push( flow::Running ); 355 | fiber.flow_points.push( flow::PopSuppressedFlow ); 356 | } 357 | 358 | opcode::EndFinally => { 359 | fiber.flow_points.pop(); 360 | let flow = fiber.suppressed_flows.pop().unwrap(); 361 | fiber.set_flow( flow ); 362 | match fiber.flow { 363 | flow::Running => {}, 364 | _ => continue 'flow_loop, 365 | }; 366 | } 367 | 368 | // Data stack operations 369 | 370 | opcode::Pop => { 371 | fiber.data_stack.pop(); 372 | } 373 | 374 | // Values 375 | 376 | opcode::PushFunction { index: i } => { 377 | 378 | let definition = fiber.frame.get_code().functions.get( i ).clone(); 379 | let mut function = function::Function::new( definition ); 380 | 381 | for binding in function.definition.bindings.iter() { 382 | match *binding { 383 | function::LocalToStaticBoundBinding( from, to ) => { 384 | let bound_var = fiber.frame.get_local_variable( from ); 385 | *function.static_bound_variables.get_mut( to ) = bound_var.clone(); 386 | } 387 | function::SharedLocalToSharedBoundBinding( from, to ) => { 388 | let bound_var = fiber.frame.get_shared_local_variable( from ).as_mut().unwrap(); 389 | *function.shared_bound_variables.get_mut( to ) = bound_var.clone(); 390 | } 391 | function::StaticBoundToStaticBoundBinding( from, to ) => { 392 | let bound_var = fiber.frame.get_static_bound_variable( from ); 393 | *function.static_bound_variables.get_mut( to ) = bound_var.clone(); 394 | } 395 | function::SharedBoundToSharedBoundBinding( from, to ) => { 396 | let bound_var = fiber.frame.get_shared_bound_variable( from ); 397 | *function.shared_bound_variables.get_mut( to ) = bound_var.clone(); 398 | } 399 | } 400 | } 401 | 402 | fiber.push_data( 403 | value::Function( vm.functions.register( function ) ) 404 | ); 405 | } 406 | 407 | opcode::PushString { index: i } => { 408 | let string = fiber.frame.get_code().strings.get( i ).clone(); 409 | fiber.push_data( value::String( string ) ); 410 | } 411 | 412 | opcode::PushFloat { value: f } => { 413 | fiber.push_data( value::Float( f ) ); 414 | } 415 | 416 | opcode::PushInteger { value: i } => { 417 | fiber.push_data( value::Integer( i ) ); 418 | } 419 | 420 | opcode::PushBoolean { value: b } => { 421 | fiber.push_data( value::Boolean( b ) ); 422 | } 423 | 424 | opcode::PushNothing => { 425 | fiber.push_data( value::Nothing ); 426 | } 427 | 428 | opcode::InlinedModule { ptr: ptr } => { 429 | fiber.push_data( value::Module( ptr ) ); 430 | } 431 | 432 | // Variables 433 | 434 | opcode::StoreLocal( i ) => { 435 | *fiber.frame.get_local_variable( i ) = fiber.pop_data(); 436 | } 437 | 438 | opcode::LoadLocal( i ) => { 439 | let variable = fiber.frame.get_local_variable( i ).clone(); 440 | fiber.push_data( variable ); 441 | } 442 | 443 | opcode::InitializeSharedLocal( i ) => { 444 | *fiber.frame.get_shared_local_variable( i ) = Some( Rc::new( value::Nothing ) ); 445 | } 446 | 447 | opcode::StoreSharedLocal( i ) => { 448 | **fiber.frame.get_shared_local_variable( i ).as_mut().unwrap() = fiber.pop_data(); 449 | } 450 | 451 | opcode::LoadSharedLocal( i ) => { 452 | let value = (**fiber.frame.get_shared_local_variable( i ).as_mut().unwrap()).clone(); 453 | fiber.push_data( value ); 454 | } 455 | 456 | opcode::StoreStaticBound( i ) => { 457 | *fiber.frame.get_static_bound_variable( i ) = fiber.pop_data(); 458 | } 459 | 460 | opcode::LoadStaticBound( i ) => { 461 | let value = (*fiber.frame.get_static_bound_variable( i )).clone(); 462 | fiber.push_data( value ); 463 | } 464 | 465 | opcode::StoreSharedBound( i ) => { 466 | **fiber.frame.get_shared_bound_variable( i ) = fiber.pop_data(); 467 | } 468 | 469 | opcode::LoadSharedBound( i ) => { 470 | let value = (**fiber.frame.get_shared_bound_variable( i )).clone(); 471 | fiber.push_data( value ); 472 | } 473 | 474 | // Names 475 | 476 | opcode::Use { operation: operation } => { 477 | let operation = unsafe { operation.get_box() }; 478 | unsafe { *opcodes.offset( fiber.frame.get_context().instruction as int ) = opcode::Nop; } 479 | 480 | fiber.frame.get_context().instruction += 1; 481 | 482 | new_frame!( frame::RustOperationFrame( 483 | operation as Box 484 | ) ); 485 | } 486 | 487 | opcode::LoadImplicit { name: name } => { 488 | match vm.implicit.find_id( name ) { 489 | Ok( value ) => { 490 | fiber.push_data( value.clone() ); 491 | } 492 | Err( err ) => { 493 | fiber.set_flow( flow::Throwing( err ) ); 494 | continue 'flow_loop; 495 | } 496 | } 497 | } 498 | 499 | // Access 500 | 501 | opcode::GetProperty { name: name } => { 502 | let left = fiber.pop_data(); 503 | handle_operation_result!( operations::get_property( &left, name ) ); 504 | } 505 | 506 | opcode::SetProperty { name: name } => { 507 | let right = fiber.pop_data(); 508 | let left = fiber.pop_data(); 509 | handle_operation_result!( operations::set_property( &left, name, &right ) ); 510 | } 511 | 512 | opcode::GetItem => { 513 | let key = fiber.pop_data(); 514 | let expression = fiber.pop_data(); 515 | (key); (expression); 516 | unimplemented!(); 517 | } 518 | 519 | // Operators 520 | 521 | opcode::Add => { 522 | let right = fiber.pop_data(); 523 | let left = fiber.pop_data(); 524 | handle_operation_result!( operations::add( &left, &right ) ); 525 | } 526 | 527 | opcode::Subtract => { 528 | let right = fiber.pop_data(); 529 | let left = fiber.pop_data(); 530 | handle_operation_result!( operations::subtract( &left, &right ) ); 531 | } 532 | 533 | opcode::Multiply => { 534 | let right = fiber.pop_data(); 535 | let left = fiber.pop_data(); 536 | handle_operation_result!( operations::multiply( &left, &right ) ); 537 | } 538 | 539 | opcode::Divide => { 540 | let right = fiber.pop_data(); 541 | let left = fiber.pop_data(); 542 | handle_operation_result!( operations::divide( &left, &right ) ); 543 | } 544 | 545 | opcode::Union => { 546 | let right = fiber.pop_data(); 547 | let left = fiber.pop_data(); 548 | handle_operation_result!( operations::union( left, right ) ); 549 | } 550 | 551 | opcode::Is => { 552 | let right = fiber.pop_data(); 553 | let left = fiber.pop_data(); 554 | handle_operation_result!( operations::is( &left, &right ) ); 555 | } 556 | 557 | opcode::Eq => { 558 | let right = fiber.pop_data(); 559 | let left = fiber.pop_data(); 560 | handle_operation_result!( operations::eq( &left, &right ) ); 561 | } 562 | 563 | opcode::Neq => { 564 | let right = fiber.pop_data(); 565 | let left = fiber.pop_data(); 566 | handle_operation_result!( operations::neq( &left, &right ) ); 567 | } 568 | 569 | opcode::Lt => { 570 | let right = fiber.pop_data(); 571 | let left = fiber.pop_data(); 572 | handle_operation_result!( operations::lt( &left, &right ) ); 573 | } 574 | 575 | opcode::Gt => { 576 | let right = fiber.pop_data(); 577 | let left = fiber.pop_data(); 578 | handle_operation_result!( operations::gt( &left, &right ) ); 579 | } 580 | 581 | opcode::LtEq => { 582 | let right = fiber.pop_data(); 583 | let left = fiber.pop_data(); 584 | handle_operation_result!( operations::lt_eq( &left, &right ) ); 585 | } 586 | 587 | opcode::GtEq => { 588 | let right = fiber.pop_data(); 589 | let left = fiber.pop_data(); 590 | handle_operation_result!( operations::gt_eq( &left, &right ) ); 591 | } 592 | 593 | opcode::Not => { 594 | unimplemented!(); 595 | } 596 | 597 | opcode::ShortCircuitAnd => { 598 | unimplemented!(); 599 | } 600 | 601 | opcode::ShortCircuitOr => { 602 | unimplemented!(); 603 | } 604 | 605 | } // match opcode 606 | 607 | fiber.frame.get_context().instruction += 1; 608 | 609 | } // 'instruction_loop 610 | 611 | } // flow::Running | flow::Catching 612 | 613 | flow::Jumping { n_flow_points: mut n_flow_points, instruction: instruction } => { 614 | 615 | while n_flow_points > 0 { 616 | 617 | match fiber.flow_points.pop().unwrap() { 618 | 619 | flow::StartCatch {..} => { 620 | // ignored, there is no throwable that needs to be caught 621 | } 622 | 623 | flow::StartFinally { instruction: i } => { 624 | fiber.suppressed_flows.push( flow::Jumping { 625 | n_flow_points: n_flow_points, 626 | instruction: instruction, 627 | } ); 628 | fiber.flow_points.push( flow::PopSuppressedFlow ); 629 | fiber.set_flow( flow::Running ); 630 | fiber.frame.get_context().instruction = i; 631 | continue 'flow_loop; 632 | } 633 | 634 | flow::PopFrame {..} 635 | | flow::PopFrameAndRestoreFlow {..} 636 | => { unreachable!(); } 637 | 638 | flow::PopSuppressedFlow => { 639 | fiber.suppressed_flows.pop(); 640 | } 641 | } 642 | 643 | n_flow_points -= 1; 644 | } 645 | 646 | } // flow::Jumping( e ) 647 | 648 | flow::Returning( value ) => { 649 | 650 | loop { 651 | match fiber.flow_points.pop().unwrap() { 652 | 653 | flow::StartCatch {..} => { 654 | // ignored, there is no throwable that needs to be caught 655 | } 656 | 657 | flow::StartFinally { instruction: i } => { 658 | fiber.suppressed_flows.push( flow::Returning( value ) ); 659 | fiber.flow_points.push( flow::PopSuppressedFlow ); 660 | fiber.set_flow( flow::Running ); 661 | fiber.frame.get_context().instruction = i; 662 | continue 'flow_loop; 663 | } 664 | 665 | flow::PopFrame { data_stack_len: n } => { 666 | fiber.pop_frame(); 667 | fiber.data_stack.truncate( n ); 668 | fiber.push_data( value ); 669 | fiber.set_flow( flow::Running ); 670 | continue 'frame_loop; 671 | } 672 | 673 | flow::PopFrameAndRestoreFlow { data_stack_len: n } => { 674 | fiber.pop_frame(); 675 | fiber.data_stack.truncate( n ); 676 | fiber.push_data( value ); 677 | fiber.restore_flow(); 678 | continue 'frame_loop; 679 | } 680 | 681 | flow::PopSuppressedFlow => { 682 | fiber.suppressed_flows.pop(); 683 | } 684 | } 685 | } 686 | 687 | } // flow::Returning( value ) 688 | 689 | flow::Throwing( throwable ) => { 690 | 691 | loop { 692 | 693 | if fiber.flow_points.len() == 0 { 694 | 695 | let mut handlers = mem::replace( &mut vm.uncaught_throwable_handlers, Vec::new() ); 696 | for handler in handlers.mut_iter() { 697 | handler.handle_uncaught_throwable( vm, throwable.clone() ); 698 | } 699 | 700 | let new_handlers = mem::replace( &mut vm.uncaught_throwable_handlers, handlers ); 701 | vm.uncaught_throwable_handlers.push_all_move( new_handlers ); 702 | 703 | return; 704 | } 705 | 706 | match fiber.flow_points.pop().unwrap() { 707 | 708 | flow::StartCatch { instruction: i } => { 709 | fiber.set_flow( flow::Catching( throwable ) ); 710 | fiber.frame.get_context().instruction = i; 711 | continue 'frame_loop; 712 | } 713 | 714 | flow::StartFinally { instruction: i } => { 715 | fiber.suppressed_flows.push( flow::Throwing( throwable ) ); 716 | fiber.flow_points.push( flow::PopSuppressedFlow ); 717 | fiber.set_flow( flow::Running ); 718 | fiber.frame.get_context().instruction = i; 719 | continue 'frame_loop; 720 | } 721 | 722 | flow::PopFrame { data_stack_len: n } 723 | | flow::PopFrameAndRestoreFlow { data_stack_len: n } => { 724 | fiber.pop_frame(); 725 | fiber.data_stack.truncate( n ); 726 | continue 'frame_loop; 727 | } 728 | 729 | flow::PopSuppressedFlow => { 730 | fiber.suppressed_flows.pop(); 731 | } 732 | } 733 | } 734 | 735 | } // flow::Throwing( e ) 736 | 737 | } // match flow 738 | } // 'flow_loop 739 | } // if is_rust else 740 | } // 'frame_loop 741 | } 742 | -------------------------------------------------------------------------------- /src/libburn/vm/bytecode/compiler.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use mem::raw::Raw; 3 | use mem::rc::Rc; 4 | use parse::{parser, node}; 5 | use lang::origin::Origin; 6 | use lang::function; 7 | use lang::value; 8 | use vm::error::Error; 9 | use vm::bytecode::code::Code; 10 | use vm::bytecode::opcode; 11 | use vm::analysis::annotation; 12 | use vm::analysis::resolution::AnalyzeResolution; 13 | use vm::analysis::allocation::AnalyzeAllocation; 14 | use vm::run::frame; 15 | use vm::repl; 16 | 17 | pub fn compile( 18 | origin: Rc>, 19 | mut repl_state: Option<&mut repl::State>, 20 | source_code: &str 21 | ) -> Result>> { 22 | 23 | let mut ast = match parser::parse( &origin, source_code ) { 24 | Ok( ast ) => ast, 25 | Err( error ) => { 26 | return Err( vec!( box error as Box ) ); 27 | } 28 | }; 29 | 30 | { 31 | let mut pass = AnalyzeResolution::new( &origin ); 32 | pass.analyze_root( &mut ast, &mut repl_state ); 33 | if pass.errors.len() > 0 { 34 | return Err( pass.errors ); 35 | } 36 | } 37 | 38 | let mut pass = AnalyzeAllocation::new(); 39 | pass.analyze_root( &mut ast, &mut repl_state ); 40 | 41 | let code = { 42 | let mut compilation = Compilation::new(); 43 | compilation.compile_root( &mut ast ); 44 | compilation.code 45 | }; 46 | 47 | debug!( { code.dump(); } ) 48 | 49 | let locals = Vec::from_elem( code.n_local_variables, value::Nothing ); 50 | let mut shared = Vec::from_elem( code.n_shared_local_variables, None ); 51 | 52 | repl_state.map( |repl_state| { 53 | for variable in ast.frame.declared_variables.iter().take( repl_state.variables.len() ) { 54 | let repl_var = repl_state.variables.find( &variable.name ).unwrap().clone(); 55 | *shared.get_mut( variable.local_storage_index ) = Some( repl_var ); 56 | } 57 | } ); 58 | 59 | Ok( frame::BurnRootFrame { 60 | origin: origin, 61 | code: code, 62 | context: frame::BurnContext::new( locals, shared ), 63 | } ) 64 | } 65 | 66 | struct Compilation { 67 | code: Box, 68 | frames: Vec>, 69 | } 70 | 71 | type Placeholder = uint; 72 | 73 | impl Compilation { 74 | 75 | fn new() -> Compilation { 76 | Compilation { 77 | code: box Code::new(), 78 | frames: Vec::new(), 79 | } 80 | } 81 | 82 | fn get_current_frame<'l>( &'l self ) -> Raw { 83 | *self.frames.last().unwrap() 84 | } 85 | 86 | fn find_bound_storage_index( &self, variable: Raw ) -> uint { 87 | self.get_current_frame().get_closure() 88 | .bindings.iter().find( |b| { b.variable == variable } ).unwrap() 89 | .storage_index 90 | } 91 | 92 | fn create_placeholder( &mut self ) -> Placeholder { 93 | let offset = self.code.opcodes.len(); 94 | self.code.opcodes.push( opcode::Nop ); 95 | offset 96 | } 97 | 98 | fn fill_in_placeholder( &mut self, offset: Placeholder, opcode: opcode::OpCode ) { 99 | *self.code.opcodes.get_mut( offset ) = opcode; 100 | } 101 | 102 | fn compile_root( &mut self, root: &mut node::Root ) { 103 | 104 | self.frames.push( Raw::new( &root.frame ) ); 105 | 106 | self.code.n_local_variables = root.frame.n_local_variables; 107 | self.code.n_shared_local_variables = root.frame.n_shared_local_variables; 108 | 109 | for statement in root.statements.mut_iter() { 110 | self.compile_statement( *statement ); 111 | } 112 | self.code.opcodes.push( opcode::ReturnNothing ); 113 | 114 | self.frames.pop(); 115 | } 116 | 117 | fn compile_function( &mut self, frame: &annotation::Frame, block: &mut [Box] ) { 118 | 119 | self.frames.push( Raw::new( frame ) ); 120 | 121 | self.code.n_local_variables = frame.n_local_variables; 122 | self.code.n_shared_local_variables = frame.n_shared_local_variables; 123 | 124 | for statement in block.mut_iter() { 125 | self.compile_statement( *statement ); 126 | } 127 | self.code.opcodes.push( opcode::ReturnNothing ); 128 | 129 | self.frames.pop(); 130 | } 131 | 132 | fn compile_statement( &mut self, statement: &mut node::Statement ) { 133 | match *statement { 134 | 135 | node::Use { 136 | path: ref path, 137 | annotation: ref mut annotation, 138 | } => { 139 | let operation = box ::lang::module::Use::new( path.clone() ); 140 | annotation.operation = Raw::new( operation ); 141 | self.code.opcodes.push( opcode::Use { operation: Raw::new( operation ) } ); 142 | unsafe { mem::forget( operation ); } 143 | } 144 | 145 | node::ExpressionStatement { 146 | expression: ref mut expression, 147 | } => { 148 | self.compile_expression( *expression ); 149 | self.code.opcodes.push( opcode::Pop ); 150 | } 151 | 152 | node::Assignment { 153 | lvalue: ref mut lvalue, 154 | rvalue: ref mut rvalue, 155 | } => { 156 | 157 | match **lvalue { 158 | 159 | node::VariableLvalue { 160 | name: _, 161 | annotation: variable, 162 | source_offset: _, 163 | } => { 164 | 165 | self.compile_expression( *rvalue ); 166 | 167 | if variable.declared_in == self.get_current_frame() { 168 | 169 | match variable.local_storage_type { 170 | annotation::storage::Local => { 171 | self.code.opcodes.push( 172 | opcode::StoreLocal( variable.local_storage_index ) 173 | ); 174 | } 175 | annotation::storage::SharedLocal => { 176 | self.code.opcodes.push( 177 | opcode::StoreSharedLocal( variable.local_storage_index ) 178 | ); 179 | } 180 | }; 181 | 182 | } else { 183 | 184 | let bound_storage_index = self.find_bound_storage_index( variable ); 185 | 186 | match variable.bound_storage_type { 187 | annotation::storage::StaticBound => { 188 | self.code.opcodes.push( 189 | opcode::StoreStaticBound( bound_storage_index ) 190 | ); 191 | } 192 | annotation::storage::SharedBound => { 193 | self.code.opcodes.push( 194 | opcode::StoreSharedBound( bound_storage_index ) 195 | ); 196 | } 197 | }; 198 | } 199 | } 200 | 201 | node::DotAccessLvalue { 202 | expression: ref mut expression, 203 | name: name, 204 | } => { 205 | self.compile_expression( *expression ); 206 | self.compile_expression( *rvalue ); 207 | self.code.opcodes.push( opcode::SetProperty { name: name } ); 208 | } 209 | } 210 | } 211 | 212 | node::Let { 213 | variable_offset: _, 214 | variable_name: _, 215 | annotation: ref annotation, 216 | default: ref mut default, 217 | } => { 218 | 219 | if default.is_some() { 220 | 221 | self.compile_expression( *default.as_mut().unwrap() ); 222 | 223 | match annotation.local_storage_type { 224 | annotation::storage::Local => { 225 | self.code.opcodes.push( 226 | opcode::StoreLocal( annotation.local_storage_index ) 227 | ); 228 | } 229 | annotation::storage::SharedLocal => { 230 | self.code.opcodes.push( 231 | opcode::InitializeSharedLocal( annotation.local_storage_index ) 232 | ); 233 | self.code.opcodes.push( 234 | opcode::StoreSharedLocal( annotation.local_storage_index ) 235 | ); 236 | } 237 | }; 238 | 239 | } else { 240 | 241 | match annotation.local_storage_type { 242 | annotation::storage::SharedLocal => { 243 | self.code.opcodes.push( 244 | opcode::InitializeSharedLocal( annotation.local_storage_index ) 245 | ); 246 | } 247 | _ => {} 248 | }; 249 | } 250 | } 251 | 252 | node::Print { 253 | expression: ref mut expression, 254 | } => { 255 | self.compile_expression( *expression ); 256 | self.code.opcodes.push( opcode::ToString ); 257 | self.code.opcodes.push( opcode::Print ); 258 | } 259 | 260 | node::Return { 261 | expression: ref mut expression, 262 | } => { 263 | match *expression { 264 | Some( ref mut expression ) => { 265 | self.compile_expression( *expression ); 266 | } 267 | None => { 268 | self.code.opcodes.push( opcode::PushNothing ); 269 | } 270 | }; 271 | self.code.opcodes.push( opcode::Return ); 272 | } 273 | 274 | node::Throw { 275 | expression: ref mut expression, 276 | } => { 277 | self.compile_expression( *expression ); 278 | self.code.opcodes.push( opcode::Throw ); 279 | } 280 | 281 | node::If { 282 | test: ref mut test, 283 | block: ref mut if_block, 284 | else_if_clauses: ref mut else_if_clauses, 285 | else_clause: ref mut else_clause, 286 | } => { 287 | 288 | let has_else_if_clauses = else_if_clauses.len() > 0; 289 | let has_else_clause = else_clause.is_some(); 290 | 291 | let mut jump_else; 292 | let mut jump_end = Vec::::new(); 293 | 294 | self.compile_expression( *test ); 295 | jump_else = self.create_placeholder(); 296 | 297 | for statement in if_block.mut_iter() { 298 | self.compile_statement( *statement ); 299 | } 300 | 301 | if has_else_if_clauses || has_else_clause { 302 | jump_end.push( self.create_placeholder() ); 303 | } 304 | 305 | let last_i = else_if_clauses.len() - 1; 306 | for (i, else_if_clause) in else_if_clauses.mut_iter().enumerate() { 307 | 308 | let is_last = ( i == last_i ); 309 | 310 | let jump = opcode::JumpIfPopFalsy { instruction: self.code.opcodes.len() }; 311 | self.fill_in_placeholder( jump_else, jump ); 312 | 313 | self.compile_expression( else_if_clause.test ); 314 | jump_else = self.create_placeholder(); 315 | 316 | for statement in else_if_clause.block.mut_iter() { 317 | self.compile_statement( *statement ); 318 | } 319 | 320 | if ! is_last || has_else_clause { 321 | jump_end.push( self.create_placeholder() ); 322 | } 323 | } 324 | 325 | let jump = opcode::JumpIfPopFalsy { instruction: self.code.opcodes.len() }; 326 | self.fill_in_placeholder( jump_else, jump ); 327 | 328 | if has_else_clause { 329 | for statement in else_clause.as_mut().unwrap().block.mut_iter() { 330 | self.compile_statement( *statement ); 331 | } 332 | } 333 | 334 | let jump = opcode::Jump { instruction: self.code.opcodes.len() }; 335 | for &placeholder in jump_end.iter() { 336 | self.fill_in_placeholder( placeholder, jump ); 337 | } 338 | } 339 | 340 | node::While { 341 | test: ref mut while_test, 342 | block: ref mut while_block, 343 | else_clause: ref mut else_clause, 344 | } => { 345 | 346 | let start = self.code.opcodes.len(); 347 | 348 | self.compile_expression( *while_test ); 349 | let test_opcode = self.create_placeholder(); 350 | 351 | for statement in while_block.mut_iter() { 352 | self.compile_statement( *statement ); 353 | } 354 | 355 | self.code.opcodes.push( opcode::Jump { instruction: start } ); 356 | 357 | let jump = opcode::JumpIfPopFalsy { instruction: self.code.opcodes.len() }; 358 | self.fill_in_placeholder( test_opcode, jump ); 359 | 360 | if else_clause.is_some() { 361 | unimplemented!(); 362 | } 363 | } 364 | 365 | node::Try { 366 | block: ref mut try_block, 367 | catch_clauses: ref mut catch_clauses, 368 | else_clause: ref mut else_clause, 369 | finally_clause: ref mut finally_clause, 370 | } => { 371 | 372 | let has_catch_clauses = catch_clauses.len() > 0; 373 | let has_else_clause = else_clause.is_some(); 374 | let has_finally_clause = finally_clause.is_some(); 375 | 376 | // TRY 377 | 378 | let push_finally = if has_finally_clause { 379 | Some( self.create_placeholder() ) 380 | } else { 381 | None 382 | }; 383 | 384 | let push_catch = if has_catch_clauses { 385 | Some( self.create_placeholder() ) 386 | } else { 387 | None 388 | }; 389 | 390 | for statement in try_block.mut_iter() { 391 | self.compile_statement( *statement ); 392 | } 393 | 394 | let mut end_catch = Vec::::new(); 395 | 396 | if has_catch_clauses { 397 | 398 | self.code.opcodes.push( opcode::PopFlowPoint ); 399 | 400 | let end_try_jump = self.create_placeholder(); 401 | 402 | let opcode = opcode::PushStartCatchFlowPoint { instruction: self.code.opcodes.len() }; 403 | self.fill_in_placeholder( push_catch.unwrap(), opcode ); 404 | 405 | for catch_clause in catch_clauses.mut_iter() { 406 | 407 | let has_type = catch_clause.type_.is_some(); 408 | let variable = catch_clause.variable; 409 | 410 | if has_type { 411 | self.compile_expression( *catch_clause.type_.as_mut().unwrap() ); 412 | self.code.opcodes.push( opcode::ThrownIs ); 413 | } 414 | 415 | let catch = self.create_placeholder(); 416 | 417 | for statement in catch_clause.block.mut_iter() { 418 | self.compile_statement( *statement ); 419 | } 420 | 421 | end_catch.push( self.create_placeholder() ); 422 | 423 | let opcode = if has_type { 424 | 425 | match variable.local_storage_type { 426 | annotation::storage::Local => { 427 | opcode::CatchLocalOrJump { 428 | storage_index: variable.local_storage_index, 429 | instruction: self.code.opcodes.len(), 430 | } 431 | } 432 | annotation::storage::SharedLocal => { 433 | opcode::CatchSharedLocalOrJump { 434 | storage_index: variable.local_storage_index, 435 | instruction: self.code.opcodes.len(), 436 | } 437 | } 438 | } 439 | 440 | } else { 441 | 442 | match variable.local_storage_type { 443 | annotation::storage::Local => { 444 | opcode::CatchLocal { 445 | storage_index: variable.local_storage_index, 446 | } 447 | } 448 | annotation::storage::SharedLocal => { 449 | opcode::CatchSharedLocal { 450 | storage_index: variable.local_storage_index, 451 | } 452 | } 453 | } 454 | 455 | }; 456 | 457 | self.fill_in_placeholder( catch, opcode ); 458 | } 459 | 460 | self.code.opcodes.push( opcode::Rethrow ); 461 | 462 | let jump = opcode::Jump { instruction: self.code.opcodes.len() }; 463 | self.fill_in_placeholder( end_try_jump, jump ); 464 | } 465 | 466 | if has_else_clause { 467 | 468 | for statement in else_clause.as_mut().unwrap().block.mut_iter() { 469 | self.compile_statement( *statement ); 470 | } 471 | } 472 | 473 | let jump = opcode::Jump { instruction: self.code.opcodes.len() }; 474 | for placeholder in end_catch.move_iter() { 475 | self.fill_in_placeholder( placeholder, jump ); 476 | } 477 | 478 | if has_finally_clause { 479 | 480 | self.code.opcodes.push( opcode::StartFinally ); 481 | 482 | let opcode = opcode::PushStartFinallyFlowPoint { instruction: self.code.opcodes.len() }; 483 | self.fill_in_placeholder( push_finally.unwrap(), opcode ); 484 | 485 | for statement in finally_clause.as_mut().unwrap().block.mut_iter() { 486 | self.compile_statement( *statement ); 487 | } 488 | 489 | self.code.opcodes.push( opcode::EndFinally ); 490 | } 491 | } 492 | } 493 | } 494 | 495 | fn compile_expression( &mut self, expression: &mut node::Expression ) { 496 | match *expression { 497 | 498 | node::Nothing => { 499 | self.code.opcodes.push( opcode::PushNothing ); 500 | } 501 | 502 | node::Boolean { 503 | value: b, 504 | } => { 505 | self.code.opcodes.push( opcode::PushBoolean { value: b } ); 506 | } 507 | 508 | node::Integer { 509 | value: i, 510 | } => { 511 | self.code.opcodes.push( opcode::PushInteger { value: i } ); 512 | } 513 | 514 | node::Float { 515 | value: f, 516 | } => { 517 | self.code.opcodes.push( opcode::PushFloat { value: f } ); 518 | } 519 | 520 | node::String { 521 | value: ref value, 522 | } => { 523 | self.code.opcodes.push( opcode::PushString { index: self.code.strings.len() } ); 524 | self.code.strings.push( Rc::new( value.clone() ) ); 525 | } 526 | 527 | node::Variable { 528 | name: _, 529 | annotation: variable, 530 | source_offset: _, 531 | } => { 532 | 533 | if variable.declared_in == self.get_current_frame() { 534 | 535 | match variable.local_storage_type { 536 | annotation::storage::Local => { 537 | self.code.opcodes.push( 538 | opcode::LoadLocal( variable.local_storage_index ) 539 | ); 540 | } 541 | annotation::storage::SharedLocal => { 542 | self.code.opcodes.push( 543 | opcode::LoadSharedLocal( variable.local_storage_index ) 544 | ); 545 | } 546 | } 547 | 548 | } else { 549 | 550 | let bound_storage_index = self.find_bound_storage_index( variable ); 551 | 552 | match variable.bound_storage_type { 553 | annotation::storage::StaticBound => { 554 | self.code.opcodes.push( 555 | opcode::LoadStaticBound( bound_storage_index ) 556 | ); 557 | } 558 | annotation::storage::SharedBound => { 559 | self.code.opcodes.push( 560 | opcode::LoadSharedBound( bound_storage_index ) 561 | ); 562 | } 563 | }; 564 | } 565 | } 566 | 567 | node::Name { 568 | identifier: identifier, 569 | annotation: annotation, 570 | } => { 571 | match annotation.resolution { 572 | annotation::Implicit => { 573 | self.code.opcodes.push( opcode::LoadImplicit { name: identifier } ); 574 | } 575 | annotation::Use( mut use_annotation ) => { 576 | use_annotation.operation.add_inline( Raw::new( self.code ), self.code.opcodes.len() ); 577 | self.code.opcodes.push( opcode::Fail ); 578 | } 579 | }; 580 | } 581 | 582 | node::DotAccess { 583 | expression: ref mut expression, 584 | name: name, 585 | } => { 586 | self.compile_expression( *expression ); 587 | self.code.opcodes.push( opcode::GetProperty { name: name } ); 588 | } 589 | 590 | node::ItemAccess { 591 | expression: ref mut expression, 592 | key_expression: ref mut key_expression, 593 | } => { 594 | self.compile_expression( *expression ); 595 | self.compile_expression( *key_expression ); 596 | self.code.opcodes.push( opcode::GetItem ); 597 | } 598 | 599 | node::Call { 600 | expression: ref mut expression, 601 | arguments: ref mut arguments, 602 | } => { 603 | 604 | self.compile_expression( *expression ); 605 | 606 | for argument in arguments.mut_iter() { 607 | self.compile_expression( *argument ); 608 | } 609 | 610 | self.code.opcodes.push( opcode::Call { n_arguments: arguments.len() } ); 611 | } 612 | 613 | node::Addition { 614 | left: ref mut left, 615 | right: ref mut right, 616 | } => { 617 | self.compile_expression( *left ); 618 | self.compile_expression( *right ); 619 | self.code.opcodes.push( opcode::Add ); 620 | } 621 | 622 | node::Subtraction { 623 | left: ref mut left, 624 | right: ref mut right, 625 | } => { 626 | self.compile_expression( *left ); 627 | self.compile_expression( *right ); 628 | self.code.opcodes.push( opcode::Subtract ); 629 | } 630 | 631 | node::Multiplication { 632 | left: ref mut left, 633 | right: ref mut right, 634 | } => { 635 | self.compile_expression( *left ); 636 | self.compile_expression( *right ); 637 | self.code.opcodes.push( opcode::Multiply ); 638 | } 639 | 640 | node::Division { 641 | left: ref mut left, 642 | right: ref mut right, 643 | } => { 644 | self.compile_expression( *left ); 645 | self.compile_expression( *right ); 646 | self.code.opcodes.push( opcode::Divide ); 647 | } 648 | 649 | node::Union { 650 | left: ref mut left, 651 | right: ref mut right, 652 | } => { 653 | self.compile_expression( *left ); 654 | self.compile_expression( *right ); 655 | self.code.opcodes.push( opcode::Union ); 656 | } 657 | 658 | node::Is { 659 | left: ref mut left, 660 | right: ref mut right, 661 | } => { 662 | self.compile_expression( *left ); 663 | self.compile_expression( *right ); 664 | self.code.opcodes.push( opcode::Is ); 665 | } 666 | 667 | node::Eq { 668 | left: ref mut left, 669 | right: ref mut right, 670 | } => { 671 | self.compile_expression( *left ); 672 | self.compile_expression( *right ); 673 | self.code.opcodes.push( opcode::Eq ); 674 | } 675 | 676 | node::Neq { 677 | left: ref mut left, 678 | right: ref mut right, 679 | } => { 680 | self.compile_expression( *left ); 681 | self.compile_expression( *right ); 682 | self.code.opcodes.push( opcode::Neq ); 683 | } 684 | 685 | node::Lt { 686 | left: ref mut left, 687 | right: ref mut right, 688 | } => { 689 | self.compile_expression( *left ); 690 | self.compile_expression( *right ); 691 | self.code.opcodes.push( opcode::Lt ); 692 | } 693 | 694 | node::Gt { 695 | left: ref mut left, 696 | right: ref mut right, 697 | } => { 698 | self.compile_expression( *left ); 699 | self.compile_expression( *right ); 700 | self.code.opcodes.push( opcode::Gt ); 701 | } 702 | 703 | node::LtEq { 704 | left: ref mut left, 705 | right: ref mut right, 706 | } => { 707 | self.compile_expression( *left ); 708 | self.compile_expression( *right ); 709 | self.code.opcodes.push( opcode::LtEq ); 710 | } 711 | 712 | node::GtEq { 713 | left: ref mut left, 714 | right: ref mut right, 715 | } => { 716 | self.compile_expression( *left ); 717 | self.compile_expression( *right ); 718 | self.code.opcodes.push( opcode::GtEq ); 719 | } 720 | 721 | node::Not { 722 | expression: ref mut expression, 723 | } => { 724 | self.compile_expression( *expression ); 725 | self.code.opcodes.push( opcode::Not ); 726 | } 727 | 728 | node::And { 729 | left: ref mut left, 730 | right: ref mut right, 731 | } => { 732 | self.compile_expression( *left ); 733 | let placeholder = self.create_placeholder(); 734 | self.compile_expression( *right ); 735 | self.fill_in_placeholder( placeholder, opcode::ShortCircuitAnd ); 736 | } 737 | 738 | node::Or { 739 | left: ref mut left, 740 | right: ref mut right, 741 | } => { 742 | self.compile_expression( *left ); 743 | let placeholder = self.create_placeholder(); 744 | self.compile_expression( *right ); 745 | self.fill_in_placeholder( placeholder, opcode::ShortCircuitOr ); 746 | } 747 | 748 | node::Function { 749 | parameters: ref parameters, 750 | frame: ref frame, 751 | block: ref mut block, 752 | } => { 753 | 754 | let mut compilation = Compilation::new(); 755 | compilation.compile_function( frame, block.as_mut_slice() ); 756 | let code = compilation.code; 757 | 758 | let mut parameter_definitions = Vec::::new(); 759 | for parameter in parameters.iter() { 760 | let variable = parameter.variable; 761 | match variable.local_storage_type { 762 | annotation::storage::Local => { 763 | parameter_definitions.push( function::FunctionParameterDefinition { 764 | name: variable.name, 765 | storage: function::LocalFunctionParameterStorage( variable.local_storage_index ), 766 | } ); 767 | } 768 | annotation::storage::SharedLocal => { 769 | parameter_definitions.push( function::FunctionParameterDefinition { 770 | name: variable.name, 771 | storage: function::SharedLocalFunctionParameterStorage( variable.local_storage_index ), 772 | } ); 773 | } 774 | }; 775 | } 776 | 777 | let mut binding_definitions = Vec::::new(); 778 | for binding in frame.closure.as_ref().unwrap().bindings.iter() { 779 | let variable = binding.variable; 780 | 781 | // local to bound 782 | if variable.declared_in == self.get_current_frame() { 783 | 784 | match variable.bound_storage_type { 785 | annotation::storage::StaticBound => { 786 | binding_definitions.push( function::LocalToStaticBoundBinding( 787 | variable.local_storage_index, 788 | binding.storage_index 789 | ) ); 790 | } 791 | annotation::storage::SharedBound => { 792 | binding_definitions.push( function::SharedLocalToSharedBoundBinding( 793 | variable.local_storage_index, 794 | binding.storage_index 795 | ) ); 796 | } 797 | }; 798 | 799 | // bound to bound 800 | } else { 801 | 802 | let current_bound_storage_index = self.find_bound_storage_index( variable ); 803 | 804 | match variable.bound_storage_type { 805 | annotation::storage::StaticBound => { 806 | binding_definitions.push( function::StaticBoundToStaticBoundBinding( 807 | current_bound_storage_index, 808 | binding.storage_index 809 | ) ); 810 | } 811 | annotation::storage::SharedBound => { 812 | binding_definitions.push( function::SharedBoundToSharedBoundBinding( 813 | current_bound_storage_index, 814 | binding.storage_index 815 | ) ); 816 | } 817 | }; 818 | } 819 | } 820 | 821 | let definition = Rc::new( function::FunctionDefinition::new( 822 | code, 823 | parameter_definitions, 824 | binding_definitions 825 | ) ); 826 | 827 | self.code.opcodes.push( opcode::PushFunction { index: self.code.functions.len() } ); 828 | self.code.functions.push( definition ); 829 | } 830 | } 831 | } 832 | } 833 | --------------------------------------------------------------------------------