Procházet zdrojové kódy

First commit

Signed-off-by: Sean Young <sean@mess.org>
Sean Young před 6 roky
revize
ea60c0116d
16 změnil soubory, kde provedl 1667 přidání a 0 odebrání
  1. 11 0
      .gitignore
  2. 16 0
      Cargo.toml
  3. 201 0
      LICENSE-APACHE
  4. 23 0
      LICENSE-MIT
  5. 109 0
      README.md
  6. 35 0
      TODO.md
  7. 11 0
      build-llvm.sh
  8. 8 0
      build.rs
  9. 289 0
      src/ast.rs
  10. 216 0
      src/emit.rs
  11. 141 0
      src/main.rs
  12. 69 0
      src/resolve.rs
  13. 479 0
      src/solidity.lalrpop
  14. 6 0
      test/compiles.sol
  15. 19 0
      test/parse1.sol
  16. 34 0
      test/parse2.sol

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+
+Cargo.lock
+/target
+**/*.rs.bk
+/src/solidity.rs
+
+/llvm
+/llvm-src
+/llvm-build
+
+*.wasm

+ 16 - 0
Cargo.toml

@@ -0,0 +1,16 @@
+[package]
+name = "solang"
+version = "0.1.0"
+authors = ["Sean Young <sean@mess.org>"]
+build = "build.rs"
+
+[build-dependencies]
+lalrpop = "0.16.1"
+
+[dependencies]
+lalrpop = "0.16.1"
+lalrpop-util = "0.16.1"
+regex = "0.2.1"
+llvm-sys = "70.0.1"
+num-bigint = "0.2"
+num-traits = "0.2.6"

+ 201 - 0
LICENSE-APACHE

@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 23 - 0
LICENSE-MIT

@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.

+ 109 - 0
README.md

@@ -0,0 +1,109 @@
+# solang - A new Solidity to wasm compiler written in rust
+
+This is solang, a new proof of concept
+[solidity](https://en.wikipedia.org/wiki/Solidity) compiler. The
+[existing solidity compiler](https://solidity.readthedocs.io/) is a huge C++
+code base which implements its own parser, optimizer and handling of binary
+files.
+
+The idea here is that we use standard tooling like a parser generator, llvm
+for its optimizer and handling of wasm binary files and use rust. As result,
+only the compiler frontend needs to be written. This will be a much smaller
+codebase which is hopefully more maintainable than the existing solidity
+compiler.
+
+In addition we will have a solidity compiler which supports wasm, which allows
+the ethereum community to move away from the EVM. This, in turn, allows us to
+improve the solidity language in ways not easily implemented in EVM, like
+string concatenation or string formatting.
+
+## What is implemented so far
+
+This is really just a starting point. So far, we can compile the following
+solidity contract:
+
+```solidity
+contract test3 {
+	function foo() returns (uint32) {
+		return 2 + 2;
+	}
+}
+```
+
+The parser is fairly complete. The resolve/annotate stage and LLVM IR conversion
+stage need work.
+
+## How to build
+
+On Ubuntu 18.10, you need:
+
+`sudo apt install curl llvm git build-essential zlib1g-dev`
+
+Earlier versions require your own build of llvm, see below.
+
+To use the lalrpop parser, solang relies on rust box_patterns. This is not
+available in rust stable channel yet, so the rustc nightly compiler must be
+used. So, install rust using [rustup](https://rustup.rs/) and then switch to
+the nightly channel using `rustup default nightly`.
+
+## llvm libraries
+
+You will need the llvm libs, compiled with the WebAssembly backend/target.
+The output of `llc --version` must include `wasm32 - WebAssembly 32-bit`. If
+it does, then `cargo build` will suffice. If not, then follow the steps
+below.
+
+The Fedora 29 and Ubuntu 18.04 llvm package does not include this; on Ubuntu
+18.10 you are in luck, and you should not need to build your own llvm
+libraries.
+
+You need the following dependencies on Ubuntu:
+
+`sudo apt install cmake ninja-build subversion build-essential`
+
+You can run the `build-llvm.sh` shell script to download llvm, compile it and
+then build solang. This will place the built llvm in the llvm/ directory.
+
+Once you have the llvm libraries built, make sure you have llvm-config in your
+path whenever you execute a cargo command. This will ensure that the right
+version is used.
+
+## How to run
+
+For now, solang just parses each command line argument as a solidity file and produces
+a *contractname*.wasm for each contract in all solidity files specified.
+
+Run:
+
+`cargo run test/compiles.sol` 
+
+This compiles this contract:
+
+```solidity
+contract test3 {
+	function foo() returns (uint32) {
+		return 2 + 2;
+	}
+}
+```
+
+And you will have a test3.wasm file generated for the test3 contract in this
+solidity contract.
+
+```
+$ wasm-objdump -d test3.wasm
+
+test3.wasm:	file format wasm 0x1
+
+Code Disassembly:
+
+000063 <foo>:
+ 000064: 41 04                      | i32.const 4
+ 000066: 0b                         | end
+```
+Note the optimising compiler at work here.
+
+## How to contribute/get in touch
+
+Have a look at our [TODO](TODO.md) or find us on the burrow channel on
+[Hyperledger Chat](https://chat.hyperledger.org).

+ 35 - 0
TODO.md

@@ -0,0 +1,35 @@
+# TODO
+
+Please note that solang so far is the result of a few days of hacking. My aim
+was to write something which can compile an extremely simple solidity contract
+to wasm.
+
+There are other rust projects that implement a compiler frontend in rust
+using llvm, for example [bfc](https://github.com/Wilfred/bfc).
+
+## Commandline:
+ * Add proper command line argument parser, so we have --help, --version, -O,
+   -Wall, --emit-llvm and -S
+
+## Parser:
+ * the AST uses positional structs, these should really be named fields
+ * Remove new() methods and make all fields public
+ * the lalrpop lexer cannot deal with comments, we need a customer lexer for this rather
+   than removing comments in the strip_comments function
+ * We should use location tracker so that warnings and errors can carry proper line and column
+   numbers
+ * Does not parse all of solidity yet
+
+## Resolver:
+ * The resolver is very bare-bones right now.
+ * Variables need to be stored in scopes and carry their types and initializers
+ * Need to walk the CFG to check for uninitialised usses
+ * Expressions need to be checked for types, add warnings and errors or casts as appropriate
+ * Custom types like mappings and structs need implementing
+
+## Code Emitter/LLVM IR conversion
+ * The code emmiter has barely started
+ * llvm-sys is unsafe and a clean safe interface would be preferable
+
+## Testing
+ * We really need something which can load and test wasm files

+ 11 - 0
build-llvm.sh

@@ -0,0 +1,11 @@
+
+
+svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm-src
+mkdir llvm-build
+cd llvm-build
+cmake -G Ninja -DLLVM_TARGETS_TO_BUILD=WebAssembly -DCMAKE_INSTALL_PREFIX=../llvm -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_ENABLE_ASSERTIONS=ON ../llvm-src
+cmake --build . --target install
+cd ..
+# ensure llvm-config is in the path
+export PATH=$(pwd)/llvm/bin:$PATH 
+cargo build

+ 8 - 0
build.rs

@@ -0,0 +1,8 @@
+extern crate lalrpop;
+
+fn main() {
+    lalrpop::Configuration::new()
+        .generate_in_source_tree()
+        .process()
+        .unwrap();
+}

+ 289 - 0
src/ast.rs

@@ -0,0 +1,289 @@
+use num_bigint::BigInt;
+use std::collections::HashMap;
+
+#[derive(Debug,PartialEq)]
+pub struct SourceUnit(
+    pub String,
+    pub Vec<SourceUnitPart>
+);
+
+#[derive(Debug,PartialEq)]
+pub enum SourceUnitPart {
+    ContractDefinition(Box<ContractDefinition>),
+    PragmaDirective(String, String),
+    ImportDirective(String)
+}
+
+#[derive(Debug,PartialEq,Clone,Copy)]
+pub enum ElementaryTypeName {
+    Address,
+    Bool,
+    String,
+    Int(u16),
+    Uint(u16),
+    Bytes(u8),
+    DynamicBytes,
+}
+
+#[derive(Debug,PartialEq)]
+pub enum StorageLocation {
+    Default,
+    Memory,
+    Storage,
+    Calldata,
+}
+
+#[derive(Debug,PartialEq)]
+pub struct VariableDeclaration(
+    pub ElementaryTypeName, 
+    pub StorageLocation,
+    pub String
+);
+
+#[derive(Debug,PartialEq)]
+pub struct StructDefinition(
+    String,
+    Vec<Box<VariableDeclaration>>
+);
+
+impl StructDefinition {
+    pub fn new(n: String, v: Vec<Box<VariableDeclaration>>) -> StructDefinition {
+        StructDefinition(n, v)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub enum ContractPart {
+    StructDefinition(Box<StructDefinition>),
+    EventDefinition(Box<EventDefinition>),
+    EnumDefinition(Box<EnumDefinition>),
+    StateVariableDeclaration(Box<StateVariableDeclaration>),
+    FunctionDefinition(Box<FunctionDefinition>),
+}
+
+#[derive(Debug,PartialEq)]
+pub enum ContractType {
+    Contract,
+    Interface,
+    Library,
+}
+
+#[derive(Debug,PartialEq)]
+pub struct ContractDefinition(
+    pub ContractType,
+    pub String,
+    pub Vec<ContractPart>,
+);
+
+#[derive(Debug,PartialEq)]
+pub struct EventParameter(
+    ElementaryTypeName,
+    bool,
+    Option<String>,
+);
+
+impl EventParameter {
+    pub fn new(t: ElementaryTypeName, indexed: bool, n: Option<String>) -> EventParameter {
+        EventParameter(t, indexed, n)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub struct EventDefinition(
+    String,
+    Vec<EventParameter>,
+    bool,
+);
+
+impl EventDefinition {
+    pub fn new(n: String, v: Vec<EventParameter>, anonymous: bool) -> EventDefinition {
+        EventDefinition(n, v, anonymous)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub struct EnumDefinition(
+    String,
+    Vec<String>,
+);
+
+impl EnumDefinition {
+    pub fn new(n: String, v: Vec<String>) -> EnumDefinition {
+        EnumDefinition(n, v)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub enum VariableAttribute {
+    Public,
+    Internal,
+    Private,
+    Constant
+}
+
+#[derive(Debug,PartialEq)]
+pub struct StateVariableDeclaration(
+    ElementaryTypeName,
+    Vec<VariableAttribute>,
+    String,
+    Option<Expression>
+);
+
+impl StateVariableDeclaration {
+    pub fn new(e: ElementaryTypeName, a: Vec<VariableAttribute>, i: String, n: Option<Expression>) -> StateVariableDeclaration {
+        StateVariableDeclaration(e, a, i, n)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub enum Expression {
+    PostIncrement(Box<Expression>),
+    PostDecrement(Box<Expression>),
+    New(ElementaryTypeName),
+    IndexAccess(Box<Expression>, Box<Option<Expression>>),
+    MemberAccess(Box<Expression>, String),
+    FunctionCall(String, Vec<Expression>),
+    Not(Box<Expression>),
+    Complement(Box<Expression>),
+    Delete(Box<Expression>),
+    PreIncrement(Box<Expression>),
+    PreDecrement(Box<Expression>),
+    UnaryPlus(Box<Expression>),
+    UnaryMinus(Box<Expression>),
+    Power(Box<Expression>, Box<Expression>),
+    Multiply(Box<Expression>, Box<Expression>),
+    Divide(Box<Expression>, Box<Expression>),
+    Modulo(Box<Expression>, Box<Expression>),
+    Add(Box<Expression>, Box<Expression>),
+    Subtract(Box<Expression>, Box<Expression>),
+    ShiftLeft(Box<Expression>, Box<Expression>),
+    ShiftRight(Box<Expression>, Box<Expression>),
+    BitwiseAnd(Box<Expression>, Box<Expression>),
+    BitwiseXor(Box<Expression>, Box<Expression>),
+    BitwiseOr(Box<Expression>, Box<Expression>),
+    Less(Box<Expression>, Box<Expression>),
+    More(Box<Expression>, Box<Expression>),
+    LessEqual(Box<Expression>, Box<Expression>),
+    MoreEqual(Box<Expression>, Box<Expression>),
+    Equal(Box<Expression>, Box<Expression>),
+    NotEqual(Box<Expression>, Box<Expression>),
+    And(Box<Expression>, Box<Expression>),
+    Or(Box<Expression>, Box<Expression>),
+    Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
+    Assign(Box<Expression>, Box<Expression>),
+    AssignOr(Box<Expression>, Box<Expression>),
+    AssignAnd(Box<Expression>, Box<Expression>),
+    AssignXor(Box<Expression>, Box<Expression>),
+    AssignShiftLeft(Box<Expression>, Box<Expression>),
+    AssignShiftRight(Box<Expression>, Box<Expression>),
+    AssignAdd(Box<Expression>, Box<Expression>),
+    AssignSubtract(Box<Expression>, Box<Expression>),
+    AssignMultiply(Box<Expression>, Box<Expression>),
+    AssignDivide(Box<Expression>, Box<Expression>),
+    AssignModulo(Box<Expression>, Box<Expression>),
+    BoolLiteral(bool),
+    NumberLiteral(BigInt),
+    StringLiteral(String),
+    Variable(String),
+}
+
+#[derive(Debug,PartialEq)]
+pub struct Parameter(
+    pub ElementaryTypeName,
+    pub Option<StorageLocation>,
+    pub Option<String>
+);
+
+impl Parameter {
+    pub fn new(e: ElementaryTypeName, s: Option<StorageLocation>, i: Option<String>) -> Parameter {
+        Parameter(e, s, i)
+    }
+}
+
+#[derive(Debug,PartialEq)]
+pub enum StateMutability {
+    Pure,
+    View,
+    Payable
+}
+
+#[derive(Debug,PartialEq)]
+pub enum FunctionAttribute {
+    StateMutability(StateMutability),
+    External,
+    Public,
+    Internal,
+    Private,
+}
+
+#[derive(Debug,PartialEq)]
+pub struct FunctionDefinition {
+    pub name: Option<String>,
+    pub params: Vec<Parameter>,
+    pub attributes: Vec<FunctionAttribute>,
+    pub returns: Vec<Parameter>,
+    pub body: Statement,
+    // annotated tree
+    pub vartable: Option<HashMap<String, ElementaryTypeName>>,
+}
+
+#[derive(Debug,PartialEq)]
+pub struct BlockStatement(
+    pub Vec<Statement>
+);
+
+#[derive(Debug,PartialEq)]
+pub enum Statement {
+    BlockStatement(BlockStatement),
+    If(Expression, Box<Statement>, Box<Option<Statement>>),
+    While(Expression, Box<Statement>),
+    PlaceHolder,
+    Expression(Expression),
+    VariableDefinition(Box<VariableDeclaration>, Option<Expression>),
+    For(Box<Option<Statement>>, Box<Option<Expression>>, Box<Option<Statement>>, Box<Option<Statement>>),
+    DoWhile(Box<Statement>, Expression),
+    Continue,
+    Break,
+    Return(Option<Expression>),
+    Throw,
+    Emit(String, Vec<Expression>),
+    Empty
+}
+
+#[cfg(test)]
+mod test {
+    use solidity;
+    use super::*;
+    
+    #[test]
+    fn parse_test() {
+        let e = solidity::SourceUnitParser::new()
+                .parse("contract foo {
+                    struct Jurisdiction {
+                        bool exists;
+                        uint keyIdx;
+                        bytes2 country;
+                        bytes32 region;
+                    }
+                    string __abba_$;
+                    int64 $thing_102;
+                }")
+                .unwrap();
+
+        let a = SourceUnit("".to_string(), vec![
+            SourceUnitPart::ContractDefinition(
+                Box::new(ContractDefinition(ContractType::Contract, "foo".to_string(), vec![
+                    ContractPart::StructDefinition(Box::new(StructDefinition("Jurisdiction".to_string(), vec![
+                        Box::new(VariableDeclaration(ElementaryTypeName::Bool, StorageLocation::Default, "exists".to_string())),
+                        Box::new(VariableDeclaration(ElementaryTypeName::Uint(256), StorageLocation::Default, "keyIdx".to_string())),
+                        Box::new(VariableDeclaration(ElementaryTypeName::Bytes(2), StorageLocation::Default, "country".to_string())),
+                        Box::new(VariableDeclaration(ElementaryTypeName::Bytes(32), StorageLocation::Default, "region".to_string()))
+                    ]))),
+                    ContractPart::StateVariableDeclaration(Box::new(StateVariableDeclaration(ElementaryTypeName::String, vec![], "__abba_$".to_string(), None))),
+                    ContractPart::StateVariableDeclaration(Box::new(StateVariableDeclaration(ElementaryTypeName::Int(64), vec![], "$thing_102".to_string(), None)))
+            ])))
+        ]);
+
+        assert_eq!(e, a);
+    }
+}

+ 216 - 0
src/emit.rs

@@ -0,0 +1,216 @@
+use ast::*;
+use std::ptr::null_mut;
+use std::ffi::{CString, CStr};
+use std::str;
+use num_traits::cast::ToPrimitive;
+
+use llvm_sys::core::*;
+use llvm_sys::prelude::*;
+use llvm_sys::target::*;
+use llvm_sys::target_machine::*;
+
+const TRIPLE: &'static [u8] = b"wasm32-unknown-unknown-wasm\0";
+
+const LLVM_FALSE: LLVMBool = 0;
+const LLVM_TRUE: LLVMBool = 1;
+
+fn target_machine() -> LLVMTargetMachineRef {
+    let mut target = null_mut();
+    let mut err_msg_ptr = null_mut();
+    unsafe {
+        if LLVMGetTargetFromTriple(TRIPLE.as_ptr() as *const _, &mut target, &mut err_msg_ptr) == LLVM_TRUE {
+            let err_msg_cstr = CStr::from_ptr(err_msg_ptr as *const _);
+            let err_msg = str::from_utf8(err_msg_cstr.to_bytes()).unwrap();
+            println!("failed to create target: {}", err_msg);
+        }
+    }
+
+    let cpu = CString::new("generic").unwrap();
+    let features = CString::new("").unwrap();
+
+    let target_machine;
+    unsafe {
+        target_machine =
+            LLVMCreateTargetMachine(target,
+                                    TRIPLE.as_ptr() as *const _,
+                                    cpu.as_ptr() as *const _,
+                                    features.as_ptr() as *const _,
+                                    LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
+                                    LLVMRelocMode::LLVMRelocDefault,
+                                    LLVMCodeModel::LLVMCodeModelDefault);
+    }
+
+    target_machine
+}
+
+pub fn emit(s: SourceUnit) {
+    let context;
+
+    unsafe {
+        LLVMInitializeWebAssemblyTargetInfo();
+        LLVMInitializeWebAssemblyTarget();
+        LLVMInitializeWebAssemblyTargetMC();
+        LLVMInitializeWebAssemblyAsmPrinter();
+        LLVMInitializeWebAssemblyAsmParser();
+        LLVMInitializeWebAssemblyDisassembler();
+
+        context = LLVMContextCreate();
+    }
+
+    let tm = target_machine();
+
+    for part in &s.1 {
+        if let SourceUnitPart::ContractDefinition(ref contract) = part {
+            let contractname = CString::new(contract.1.to_string()).unwrap();
+            let filename = CString::new(contract.1.to_string() + ".wasm").unwrap();
+
+            unsafe {
+                let module = LLVMModuleCreateWithName(contractname.as_ptr());
+                LLVMSetTarget(module, TRIPLE.as_ptr() as *const _);
+                let mut builder = LLVMCreateBuilderInContext(context);
+                let mut obj_error = null_mut();
+
+                for m in &contract.2 {
+                    if let ContractPart::FunctionDefinition(ref func) = m {
+                        if let Err(s) = emit_func(func, context, module, builder) {
+                            println!("failed to compile: {}", s);
+                        }
+                    }
+                }
+                let result = LLVMTargetMachineEmitToFile(tm,
+                                                        module,
+                                                        filename.as_ptr() as *mut i8,
+                                                        LLVMCodeGenFileType::LLVMObjectFile,
+                                                        &mut obj_error);
+
+                if result != 0 {
+                    println!("obj_error: {:?}", CStr::from_ptr(obj_error as *const _));
+                }
+
+                LLVMDisposeBuilder(builder);
+                LLVMDisposeModule(module);
+            }
+        }
+    }
+
+    unsafe {
+        LLVMContextDispose(context);
+        LLVMDisposeTargetMachine(tm);
+    }
+}
+
+unsafe fn emit_func(f: &FunctionDefinition, context: LLVMContextRef, module: LLVMModuleRef, builder: LLVMBuilderRef) -> Result<(), String> {
+    if !f.params.is_empty() {
+        return Err("functions with arguments not implemented yet".to_string());
+    }
+
+    let fname = match f.name {
+        None => {
+            return Err("function with no name are not implemented yet".to_string());
+        },
+        Some(ref n) => {
+            CString::new(n.to_string()).unwrap()
+        }
+    };
+ 
+    let ret;
+
+    if f.returns.len() > 1 {
+        return Err("only functions with one return value implemented`".to_string());
+    }
+
+    if f.returns.len() == 0 {
+        ret = LLVMVoidType();
+    } else {
+        ret = match f.returns[0].0 {
+            ElementaryTypeName::Bool => LLVMInt1Type(),
+            ElementaryTypeName::Uint(n) => LLVMIntType(n as u32),
+            ElementaryTypeName::Int(n) => LLVMIntType(n as u32),
+            _ => {
+                return Err(format!("{:?} not supported", f.returns[0].0));
+            }
+        };
+    }
+
+    let mut args = vec!();
+
+    let ftype = LLVMFunctionType(ret, args.as_mut_ptr(), 0, 0);
+
+    let function = LLVMAddFunction(module, fname.as_ptr(), ftype);
+
+    let bb = LLVMAppendBasicBlockInContext(context, function, b"entry\0".as_ptr() as *const _);
+
+    LLVMPositionBuilderAtEnd(builder, bb);
+
+    f.body.emit(builder)
+}
+
+impl Statement {
+    fn emit(&self, builder: LLVMBuilderRef) -> Result<(), String> {
+        match self {
+            Statement::BlockStatement(block) => {
+                for st in &block.0 {
+                    if let Err(s) = st.emit(builder) {
+                        return Err(s);
+                    }
+                }
+            },
+            Statement::Return(None) => {
+                unsafe {
+                    LLVMBuildRetVoid(builder);
+                }
+            }
+            Statement::Return(Some(expr)) => {
+                match expr.emit(builder) {
+                    Err(s) => return Err(s),
+                    Ok(e) => unsafe {
+                        LLVMBuildRet(builder, e);
+                    }
+                }
+            },
+            Statement::Empty => {
+                // nop
+            },
+            _ => {
+                return Err(format!("statement not implement: {:?}", self));
+            }
+        }
+        
+        Ok(())
+    }
+}
+
+impl Expression {
+    fn emit(&self, builder: LLVMBuilderRef) -> Result<LLVMValueRef, String> {
+        match self {
+            Expression::NumberLiteral(n) => {
+                match n.to_u64() {
+                    None => Err(format!("failed to convert {}", n)),
+                    Some(n) =>  unsafe {
+                        Ok(LLVMConstInt(LLVMInt32Type(), n, LLVM_FALSE))
+                    }
+                }
+            },
+            Expression::Add(l, r) => {
+                let left;
+                let right;
+
+                match l.emit(builder) {
+                    Err(s) => return Err(s),
+                    Ok(l) => left = l
+                }
+                match r.emit(builder) {
+                    Err(s) => return Err(s),
+                    Ok(r) => right = r
+                }
+
+                unsafe {
+                    Ok(LLVMBuildAdd(builder, left, right, b"\0".as_ptr() as *const _))
+                }
+            },
+            _ => {
+                Err(format!("expression not implemented: {:?}", self))
+            }
+        }       
+    }
+}

+ 141 - 0
src/main.rs

@@ -0,0 +1,141 @@
+#![feature(box_patterns)]
+
+extern crate lalrpop;
+extern crate num_bigint;
+extern crate lalrpop_util;
+extern crate llvm_sys;
+extern crate num_traits;
+
+mod ast;
+mod solidity;
+mod resolve;
+mod emit;
+
+use std::env;
+use std::fs::File;
+use std::io::prelude::*;
+use lalrpop_util::ParseError;
+
+fn main() {
+    for filename in env::args().skip(1) {
+        let mut f = File::open(&filename).expect("file not found");
+
+        let mut contents = String::new();
+        f.read_to_string(&mut contents)
+            .expect("something went wrong reading the file");
+
+
+        // parse phase
+        let nocomments = strip_comments(&contents);
+
+        let s = solidity::SourceUnitParser::new()
+            .parse(&nocomments);
+
+        let mut past;
+
+        match s {
+            Ok(s) => past = s,
+            Err(e) => {
+                match e {
+                    ParseError::InvalidToken{location} => println!("{}: error: invalid token token at {}", filename, offset_to_line_column(&contents, location)),
+                    ParseError::UnrecognizedToken{token, expected} => {
+                        match token {
+                            None => println!("{}: error: unrecognised token, expected {}", filename, expected.join(",")),
+                            Some(t) => println!("{}: error: unrecognised token `{}' from {} to {}", filename, t.1, offset_to_line_column(&contents, t.0), offset_to_line_column(&contents, t.2)),
+                        }
+                    },
+                    ParseError::User{error} => {
+                        println!("{}: error: {}", filename, error)
+                    },
+                    ParseError::ExtraToken{token} => {
+                        println!("{}: extra token `{}' encountered at {}-{}", filename, token.1, token.0, token.2)
+                    }
+                }
+                return;
+            }
+        }
+
+        past.0 = filename;
+
+        // resolve phase
+        resolve::resolve(&mut past);
+
+        // emit phase
+        emit::emit(past);
+    }
+}
+
+fn offset_to_line_column(s: &String, offset: usize) -> String {
+    let mut line = 1;
+    let mut column = 1;
+
+    for (o, c) in s.char_indices() {
+        if o == offset {
+            break;
+        }
+        if c == '\n' {
+            line += 1;
+            column = 1;
+        } else {
+            column += 1;
+        }
+    }
+
+    format!("{}:{}", line, column)
+}
+
+//
+// The lalrpop lexer cannot deal with comments, so you have to write your own lexer.
+// Rather than do that let's just strip the comments before passing it to the lexer
+// It's not great code but it's a stop-gap solution anyway
+fn strip_comments(s: &String) -> String {
+    let mut n = String::new();
+    let mut single_line = false;
+    let mut multi_line = false;
+    let mut last = '\0';
+    let mut c = '\0';
+
+    for (i, j) in s.char_indices() {
+        c = j;
+        if single_line {
+            if c == '\n' {
+                single_line = false;
+            }
+            last = ' ';
+        } else if multi_line {
+            if last == '*' && c == '/' {
+                c = ' ';
+                multi_line = false;
+            }
+            if last != '\n' {
+                last = ' ';
+            }
+        } else if last == '/' && c == '/'  {
+            single_line = true;
+            last = ' ';
+        } else if last == '/' && c == '*'  {
+            multi_line = true;
+            last = ' ';
+        }
+
+        if i > 0 {
+            n.push(last);
+        }
+        last = c;
+    }
+
+    if !single_line && !multi_line {
+        n.push(c);
+    }
+
+    n
+}
+
+#[test]
+fn strip_comments_test() {
+    assert_eq!(strip_comments(&("foo //Zabc\nbar".to_string())),
+                              "foo       \nbar");
+    assert_eq!(strip_comments(&("foo /*|x\ny&*/ bar".to_string())),
+                              "foo     \n     bar");
+}
+

+ 69 - 0
src/resolve.rs

@@ -0,0 +1,69 @@
+
+use ast::*;
+use std::collections::HashMap;
+
+pub fn resolve(s: &mut SourceUnit) {
+    for p in &mut s.1 {
+        if let SourceUnitPart::ContractDefinition(ref mut def) = p {
+            if def.0 == ContractType::Contract {
+                for m in &mut def.2 {
+                    if let ContractPart::FunctionDefinition(ref mut func) = m {
+                        resolve_func(func);
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn visit_statement(s: &Statement, f: &mut FnMut(&Statement)) {
+    f(s);
+    
+    match s {
+        Statement::BlockStatement(BlockStatement(bs)) => {
+            for i in bs {
+                visit_statement(&i, f);
+            }
+        },
+        Statement::For(i, _, n, b) => {
+            if let box Some(j) = i {
+                visit_statement(&j, f);
+            }
+            if let box Some(j) = n {
+                visit_statement(&j, f);
+            }
+            if let box Some(j) = b {
+                visit_statement(&j, f);
+            }
+        },
+        Statement::While(_, b) => {
+            visit_statement(&b, f);
+        },
+        Statement::If(_, then, _else) => {
+            visit_statement(&then, f);
+            if let box Some(b) = _else {
+                visit_statement(&b, f);
+            }
+        },
+        _ => ()
+    }
+}
+
+fn resolve_func(f: &mut Box<FunctionDefinition>) {
+    // find all the variables
+    let mut vartable = HashMap::new();
+
+    visit_statement(&f.body, &mut |s| {
+        if let Statement::VariableDefinition(v, _) = s {
+            let name = &v.2;
+
+            if vartable.contains_key(name) {
+                println!("variable {} redeclared", name);
+            } else {
+                vartable.insert(name.to_string(), v.0);
+            }
+        }
+    });
+
+    f.vartable = Some(vartable);
+}

+ 479 - 0
src/solidity.lalrpop

@@ -0,0 +1,479 @@
+
+use std::str::FromStr;
+use num_bigint::BigInt;
+use ast::*;
+
+grammar;
+
+pub SourceUnit: SourceUnit = {
+    <u:SourceUnitPart+> => SourceUnit("".to_string(), u)
+}
+
+SourceUnitPart: SourceUnitPart = {
+    ContractDefinition => SourceUnitPart::ContractDefinition(<>),
+    PragmaDirective => SourceUnitPart::PragmaDirective(<>.0, <>.1),
+    ImportDirective => SourceUnitPart::ImportDirective(<>),
+}
+
+ImportDirective: String = {
+    "import" <s:StringLiteral> ";" => s
+}
+
+PragmaDirective: (String, String) = {
+    "pragma" <i:Identifier> <s:PragmaValue> ";" => (i, s)
+}
+
+// This is actually defined as [^;]*, however that causes ambiguity for the lexer. So define it
+// in a much more restricted way. Hopefully lalrpop will restrict the lexer to only those tokens
+// possible during parsing.
+PragmaValue: String = {
+    r"\^0\.\d+\.\d+" => <>.to_string()
+}
+
+ElementaryTypeName: ElementaryTypeName = {
+    "bool" => ElementaryTypeName::Bool,
+    "address" => ElementaryTypeName::Address,
+    "string" => ElementaryTypeName::String,
+    "uint" => ElementaryTypeName::Uint(256),
+    "uint8" => ElementaryTypeName::Uint(8),
+    // for i in $(seq 8 8 256); do printf '    "uint%d" => ElementaryTypeName::Uint(%d),\n' $i $i; done
+    "uint16" => ElementaryTypeName::Uint(16),
+    "uint24" => ElementaryTypeName::Uint(24),
+    "uint32" => ElementaryTypeName::Uint(32),
+    "uint40" => ElementaryTypeName::Uint(40),
+    "uint48" => ElementaryTypeName::Uint(48),
+    "uint56" => ElementaryTypeName::Uint(56),
+    "uint64" => ElementaryTypeName::Uint(64),
+    "uint72" => ElementaryTypeName::Uint(72),
+    "uint80" => ElementaryTypeName::Uint(80),
+    "uint88" => ElementaryTypeName::Uint(88),
+    "uint96" => ElementaryTypeName::Uint(96),
+    "uint104" => ElementaryTypeName::Uint(104),
+    "uint112" => ElementaryTypeName::Uint(112),
+    "uint120" => ElementaryTypeName::Uint(120),
+    "uint128" => ElementaryTypeName::Uint(128),
+    "uint136" => ElementaryTypeName::Uint(136),
+    "uint144" => ElementaryTypeName::Uint(144),
+    "uint152" => ElementaryTypeName::Uint(152),
+    "uint160" => ElementaryTypeName::Uint(160),
+    "uint168" => ElementaryTypeName::Uint(168),
+    "uint176" => ElementaryTypeName::Uint(176),
+    "uint184" => ElementaryTypeName::Uint(184),
+    "uint192" => ElementaryTypeName::Uint(192),
+    "uint200" => ElementaryTypeName::Uint(200),
+    "uint208" => ElementaryTypeName::Uint(208),
+    "uint216" => ElementaryTypeName::Uint(216),
+    "uint224" => ElementaryTypeName::Uint(224),
+    "uint232" => ElementaryTypeName::Uint(232),
+    "uint240" => ElementaryTypeName::Uint(240),
+    "uint248" => ElementaryTypeName::Uint(248),
+    "uint256" => ElementaryTypeName::Uint(256),
+    "int" => ElementaryTypeName::Int(256),
+    "int8" => ElementaryTypeName::Int(8),
+    "int16" => ElementaryTypeName::Int(16),
+    "int24" => ElementaryTypeName::Int(24),
+    "int32" => ElementaryTypeName::Int(32),
+    "int40" => ElementaryTypeName::Int(40),
+    "int48" => ElementaryTypeName::Int(48),
+    "int56" => ElementaryTypeName::Int(56),
+    "int64" => ElementaryTypeName::Int(64),
+    "int72" => ElementaryTypeName::Int(72),
+    "int80" => ElementaryTypeName::Int(80),
+    "int88" => ElementaryTypeName::Int(88),
+    "int96" => ElementaryTypeName::Int(96),
+    "int104" => ElementaryTypeName::Int(104),
+    "int112" => ElementaryTypeName::Int(112),
+    "int120" => ElementaryTypeName::Int(120),
+    "int128" => ElementaryTypeName::Int(128),
+    "int136" => ElementaryTypeName::Int(136),
+    "int144" => ElementaryTypeName::Int(144),
+    "int152" => ElementaryTypeName::Int(152),
+    "int160" => ElementaryTypeName::Int(160),
+    "int168" => ElementaryTypeName::Int(168),
+    "int176" => ElementaryTypeName::Int(176),
+    "int184" => ElementaryTypeName::Int(184),
+    "int192" => ElementaryTypeName::Int(192),
+    "int200" => ElementaryTypeName::Int(200),
+    "int208" => ElementaryTypeName::Int(208),
+    "int216" => ElementaryTypeName::Int(216),
+    "int224" => ElementaryTypeName::Int(224),
+    "int232" => ElementaryTypeName::Int(232),
+    "int240" => ElementaryTypeName::Int(240),
+    "int248" => ElementaryTypeName::Int(248),
+    "int256" => ElementaryTypeName::Int(256),
+    "byte" => ElementaryTypeName::Bytes(1),
+    "bytes" => ElementaryTypeName::DynamicBytes,
+    "bytes1" => ElementaryTypeName::Bytes(1),
+    "bytes2" => ElementaryTypeName::Bytes(2),
+    "bytes3" => ElementaryTypeName::Bytes(3),
+    "bytes4" => ElementaryTypeName::Bytes(4),
+    "bytes5" => ElementaryTypeName::Bytes(5),
+    "bytes6" => ElementaryTypeName::Bytes(6),
+    "bytes7" => ElementaryTypeName::Bytes(7),
+    "bytes8" => ElementaryTypeName::Bytes(8),
+    "bytes9" => ElementaryTypeName::Bytes(9),
+    "bytes10" => ElementaryTypeName::Bytes(10),
+    "bytes11" => ElementaryTypeName::Bytes(11),
+    "bytes12" => ElementaryTypeName::Bytes(12),
+    "bytes13" => ElementaryTypeName::Bytes(13),
+    "bytes14" => ElementaryTypeName::Bytes(14),
+    "bytes15" => ElementaryTypeName::Bytes(15),
+    "bytes16" => ElementaryTypeName::Bytes(16),
+    "bytes17" => ElementaryTypeName::Bytes(17),
+    "bytes18" => ElementaryTypeName::Bytes(18),
+    "bytes19" => ElementaryTypeName::Bytes(19),
+    "bytes20" => ElementaryTypeName::Bytes(20),
+    "bytes21" => ElementaryTypeName::Bytes(21),
+    "bytes22" => ElementaryTypeName::Bytes(22),
+    "bytes23" => ElementaryTypeName::Bytes(23),
+    "bytes24" => ElementaryTypeName::Bytes(24),
+    "bytes25" => ElementaryTypeName::Bytes(25),
+    "bytes26" => ElementaryTypeName::Bytes(26),
+    "bytes27" => ElementaryTypeName::Bytes(27),
+    "bytes28" => ElementaryTypeName::Bytes(28),
+    "bytes29" => ElementaryTypeName::Bytes(29),
+    "bytes30" => ElementaryTypeName::Bytes(30),
+    "bytes31" => ElementaryTypeName::Bytes(31),
+    "bytes32" => ElementaryTypeName::Bytes(32),
+}
+
+StorageLocation: StorageLocation = {
+    "memory" => StorageLocation::Memory,
+    "storage" => StorageLocation::Storage,
+    "calldata" => StorageLocation::Calldata,
+}
+
+Identifier: String = {
+    r"[a-zA-Z_$][a-zA-Z_$0-9]*" => <>.to_string()
+}
+
+VariableDeclaration: Box<VariableDeclaration> = {
+    ElementaryTypeName StorageLocation Identifier => Box::new(VariableDeclaration(<>)),
+    <t:ElementaryTypeName> <i:Identifier> => Box::new(VariableDeclaration(t, StorageLocation::Default, i))
+}
+
+StructDefinition: Box<StructDefinition> = {
+    "struct" <n:Identifier> "{" <v:(<VariableDeclaration> ";")+> "}" => {
+        Box::new(StructDefinition::new(n, v))
+    }
+}
+
+ContractType: ContractType = {
+    "contract" => ContractType::Contract,
+    "interface" => ContractType::Interface,
+    "library" => ContractType::Library,
+}
+
+ContractPart: ContractPart = {
+    StructDefinition => ContractPart::StructDefinition(<>),
+    EventDefinition => ContractPart::EventDefinition(<>),
+    EnumDefinition => ContractPart::EnumDefinition(<>),
+    StateVariableDeclaration => ContractPart::StateVariableDeclaration(<>),
+    FunctionDefinition => ContractPart::FunctionDefinition(<>),
+}
+
+ContractDefinition: Box<ContractDefinition> = {
+    <t:ContractType> <n:Identifier> "{" <p:(<ContractPart>)+> "}" => {
+        Box::new(ContractDefinition(t, n, p))
+    }
+}
+
+EventParameter: EventParameter = {
+    <t:ElementaryTypeName>  <i:"indexed"?> <n:Identifier?> => EventParameter::new(t, match i { Some(_) => true, _ => false }, n)
+}
+
+EventDefinition: Box<EventDefinition> = {
+    "event" <n:Identifier> "(" <v:(<EventParameter> ",")*> <e:EventParameter> ")" <a:"anonymous"?> ";" => {
+        let mut v = v;
+        v.push(e);
+        Box::new(EventDefinition::new(n, v, match a { Some(_) => true, _ => false}))
+    },
+}
+
+EnumDefinition: Box<EnumDefinition> = {
+    "enum" <n:Identifier> "{" <v:(<Identifier> ",")*> <e:Identifier> "}" => {
+        let mut v = v;
+        v.push(e);
+        Box::new(EnumDefinition::new(n, v))
+    }
+}
+
+StateVariableDeclaration: Box<StateVariableDeclaration> = {
+    <t:ElementaryTypeName> <a:VariableAttribute*> <i:Identifier> <e:("=" <Expression>)?> ";" => {
+        Box::new(StateVariableDeclaration::new(t, a, i, e))
+    }
+}
+
+VariableAttribute: VariableAttribute = {
+    "public" => VariableAttribute::Public,
+    "internal" => VariableAttribute::Internal,
+    "private" => VariableAttribute::Private,
+    "constant" => VariableAttribute::Constant
+}
+
+Expression: Expression = {
+    <e:Expression> "++" => Expression::PostIncrement(Box::new(e)),
+    <e:Expression> "--" => Expression::PostDecrement(Box::new(e)),
+    "new" <t:ElementaryTypeName> => Expression::New(t),
+    <e:Expression> "[" <i:Expression?> "]" => Expression::IndexAccess(Box::new(e), Box::new(i)),
+    <e:Expression> "." <i:Identifier> => Expression::MemberAccess(Box::new(e), i),
+    <i:Identifier> "(" ")" => {
+        Expression::FunctionCall(i, Vec::new())
+    },
+    <i:Identifier> "(" <v:(<Expression> ",")+> <e:Expression> ")" => {
+        let mut v = v;
+        v.push(e);
+        Expression::FunctionCall(i, v)
+    },
+    Precedence2,
+}
+
+Precedence2: Expression = {
+    "!" <e:Precedence3> => Expression::Not(Box::new(e)),
+    "~" <e:Precedence3> => Expression::Complement(Box::new(e)),
+    "delete" <e:Precedence3> => Expression::Delete(Box::new(e)),
+    "++" <e:Precedence3> => Expression::PreIncrement(Box::new(e)),
+    "--" <e:Precedence3> => Expression::PreDecrement(Box::new(e)),
+    "+" <e:Precedence3> => Expression::UnaryPlus(Box::new(e)),
+    "-" <e:Precedence3> => Expression::UnaryMinus(Box::new(e)),
+    Precedence3,
+}
+
+Precedence3: Expression = {
+    <l:Precedence4> "**" <r:Precedence4> => Expression::Power(Box::new(l), Box::new(r)),
+    Precedence4,
+}
+
+Precedence4: Expression = {
+    <l:Precedence5> "*" <r:Precedence5> => Expression::Multiply(Box::new(l), Box::new(r)),
+    <l:Precedence5> "/" <r:Precedence5> => Expression::Divide(Box::new(l), Box::new(r)),
+    <l:Precedence5> "%" <r:Precedence5> => Expression::Modulo(Box::new(l), Box::new(r)),
+    Precedence5,
+}
+
+Precedence5: Expression = {
+    <l:Precedence6> "+" <r:Precedence6> => Expression::Add(Box::new(l), Box::new(r)),
+    <l:Precedence6> "-" <r:Precedence6> => Expression::Subtract(Box::new(l), Box::new(r)),
+    Precedence6,
+}
+
+Precedence6: Expression = {
+    <l:Precedence6> "<<" <r:Precedence7> => Expression::ShiftLeft(Box::new(l), Box::new(r)),
+    <l:Precedence6> ">>" <r:Precedence7> => Expression::ShiftRight(Box::new(l), Box::new(r)),
+    Precedence7,
+}
+
+Precedence7: Expression = {
+    <l:Precedence7> "&" <r:Precedence8> => Expression::BitwiseAnd(Box::new(l), Box::new(r)),
+    Precedence8,
+}
+
+Precedence8: Expression = {
+    <l:Precedence8> "^" <r:Precedence9> => Expression::BitwiseXor(Box::new(l), Box::new(r)),
+    Precedence9,
+}
+
+Precedence9: Expression = {
+    <l:Precedence10> "|" <r:Precedence11> => Expression::BitwiseOr(Box::new(l), Box::new(r)),
+    Precedence10,
+}
+
+Precedence10: Expression = {
+    <l:Precedence10> "<" <r:Precedence11> => Expression::Less(Box::new(l), Box::new(r)),
+    <l:Precedence10> ">" <r:Precedence11> => Expression::More(Box::new(l), Box::new(r)),
+    <l:Precedence10> "<=" <r:Precedence11> => Expression::LessEqual(Box::new(l), Box::new(r)),
+    <l:Precedence10> ">=" <r:Precedence11> => Expression::MoreEqual(Box::new(l), Box::new(r)),
+    Precedence11,
+}
+
+Precedence11: Expression = {
+    <l:Precedence11> "==" <r:Precedence12> => Expression::Equal(Box::new(l), Box::new(r)),
+    <l:Precedence11> "!=" <r:Precedence12> => Expression::NotEqual(Box::new(l), Box::new(r)),
+    Precedence12,
+}
+
+Precedence12: Expression = {
+    <l:Precedence12> "&&" <r:Precedence13> => Expression::And(Box::new(l), Box::new(r)),
+    Precedence13,
+}
+
+Precedence13: Expression = {
+    <l:Precedence13> "||" <r:Precedence14> => Expression::Or(Box::new(l), Box::new(r)),
+    Precedence14,
+}
+
+Precedence14: Expression = {
+    <c:Precedence14> "?" <l:Precedence15> ":" <r:Precedence15> => Expression::Ternary(Box::new(c), Box::new(l), Box::new(r)),
+    Precedence15,
+}
+
+Precedence15: Expression = {
+    <l:Precedence15> "=" <r:Precedence16> => Expression::Assign(Box::new(l), Box::new(r)),
+    <l:Precedence15> "|=" <r:Precedence16> => Expression::AssignOr(Box::new(l), Box::new(r)),
+    <l:Precedence15> "^=" <r:Precedence16> => Expression::AssignXor(Box::new(l), Box::new(r)),
+    <l:Precedence15> "&=" <r:Precedence16> => Expression::AssignAnd(Box::new(l), Box::new(r)),
+    <l:Precedence15> "<<=" <r:Precedence16> => Expression::AssignShiftLeft(Box::new(l), Box::new(r)),
+    <l:Precedence15> ">>=" <r:Precedence16> => Expression::AssignShiftRight(Box::new(l), Box::new(r)),
+    <l:Precedence15> "+=" <r:Precedence16> => Expression::AssignAdd(Box::new(l), Box::new(r)),
+    <l:Precedence15> "-=" <r:Precedence16> => Expression::AssignSubtract(Box::new(l), Box::new(r)),
+    <l:Precedence15> "*=" <r:Precedence16> => Expression::AssignMultiply(Box::new(l), Box::new(r)),
+    <l:Precedence15> "/=" <r:Precedence16> => Expression::AssignDivide(Box::new(l), Box::new(r)),
+    <l:Precedence15> "%=" <r:Precedence16> => Expression::AssignModulo(Box::new(l), Box::new(r)),
+    Precedence16,
+}
+
+Precedence16: Expression = {
+    "true" => Expression::BoolLiteral(true),
+    "false" => Expression::BoolLiteral(false),
+    StringLiteral => Expression::StringLiteral(<>.to_string()),
+    <i:Identifier> => Expression::Variable(i),
+    "(" <e:Expression> ")" => e,
+    r#"-?[0-9]+"# => Expression::NumberLiteral(BigInt::from_str(<>).unwrap())
+}
+
+StringLiteral: String = {
+    r#""([^"\r\n\\]|'\\'.)*""# => <>.to_string()
+}
+
+Parameter: Parameter = {
+    <e:ElementaryTypeName> <s:StorageLocation?> <i:Identifier?> => {
+        Parameter::new(e, s, i)
+    }
+}
+
+ParameterList: Vec<Parameter> = {
+    "(" ")" => Vec::new(),
+    "(" <v:(<Parameter> ",")*> <e:Parameter> ")" => {
+        let mut v = v;
+        v.push(e);
+        v
+    }
+}
+
+StateMutability: StateMutability = {
+    "pure" => StateMutability::Pure,
+    "view" => StateMutability::View,
+    "payable" => StateMutability::Payable
+}
+
+FunctionAttribute: FunctionAttribute = {
+    StateMutability => FunctionAttribute::StateMutability(<>),
+    "external" => FunctionAttribute::External,
+    "public" => FunctionAttribute::Public,
+    "internal" => FunctionAttribute::Internal,
+    "private" => FunctionAttribute::Private
+}
+
+FunctionDefinition: Box<FunctionDefinition> = {
+    "function" <n:Identifier?> <a:ParameterList> <t:FunctionAttribute*> <r:("returns" <ParameterList>)?> ";" => {
+        let returns = match r {
+            None => vec!(),
+            Some(v) => v,
+        };
+        
+        Box::new(FunctionDefinition{
+            name: n,
+            params: a,
+            attributes: t,
+            returns: returns,
+            body: Statement::Empty,
+            vartable: None,
+        })
+    },
+    "function" <n:Identifier?> <a:ParameterList> <t:FunctionAttribute*> <r:("returns" <ParameterList>)?> <b:BlockStatement> => {
+        let returns = match r {
+            None => vec!(),
+            Some(v) => v,
+        };
+        
+        Box::new(FunctionDefinition{
+            name: n,
+            params: a,
+            attributes: t,
+            returns: returns,
+            body: Statement::BlockStatement(b),
+            vartable: None,
+        })
+    }
+}
+
+BlockStatement: BlockStatement = {
+    "{" <s:Statement*> "}" => {
+        BlockStatement(s)
+    },
+}
+
+OpenStatement: Statement = {
+    "if" "(" <cond:Expression> ")" <body:Statement> => {
+        Statement::If(cond, Box::new(body), Box::new(None))
+    },
+    "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:OpenStatement> => {
+        Statement::If(cond, Box::new(body), Box::new(Some(o)))
+    },
+    "while" "(" <e:Expression> ")" <b:OpenStatement> => {
+        Statement::While(e, Box::new(b))
+    }
+}
+
+ClosedStatement: Statement = {
+    NonIfStatement,
+    "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:ClosedStatement> => {
+        Statement::If(cond, Box::new(body), Box::new(Some(o)))
+    },
+    "while" "(" <e:Expression> ")" <b:ClosedStatement> => {
+        Statement::While(e, Box::new(b))
+    },
+    "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Statement?> ")" <block:ClosedStatement> => {
+        Statement::For(Box::new(b), Box::new(c), Box::new(n), Box::new(Some(block)))
+    },
+    "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Statement?> ")" ";" => {
+        Statement::For(Box::new(b), Box::new(c), Box::new(n), Box::new(None))
+    }
+}
+
+Statement: Statement = {
+    OpenStatement,
+    ClosedStatement
+}
+
+SimpleStatement: Statement = {
+    <v:VariableDeclaration> <e:("=" <Expression>)?> => {
+        Statement::VariableDefinition(v, e)
+    },
+    <e:Expression> => {
+        Statement::Expression(e)
+    }
+}
+
+NonIfStatement: Statement = {
+    BlockStatement => {
+        Statement::BlockStatement(<>)
+    },
+    <b:SimpleStatement> ";" => b,
+    "_" ";" => {
+        Statement::PlaceHolder
+    },
+    "do" <b:Statement> "while" "(" <e:Expression> ")" => {
+        Statement::DoWhile(Box::new(b), e)
+    },
+    "continue" ";" => {
+        Statement::Continue
+    },
+    "break" ";" => {
+        Statement::Break
+    },
+    "return" <e:Expression?> ";" => {
+        Statement::Return(e)
+    },
+    "throw" ";" => {
+        Statement::Throw
+    },
+    "emit" <n:Identifier> "(" ")" ";" => {
+        Statement::Emit(n, Vec::new())
+    },
+    "emit" <n:Identifier> "(" <v:(<Expression> ",")+> <e:Expression> ")" ";" => {
+        let mut v = v;
+        v.push(e);
+        Statement::Emit(n, v)
+    }
+}

+ 6 - 0
test/compiles.sol

@@ -0,0 +1,6 @@
+
+contract test3 {
+	function foo() returns (uint32) {
+		return 2 + 2;
+	}
+}

+ 19 - 0
test/parse1.sol

@@ -0,0 +1,19 @@
+pragma solidity ^0.4.25;
+
+contract test2 {
+	function func1() public pure returns (uint r) {
+		{
+			uint j = 10;
+		}
+
+		for (string x;1 <0; i++)
+		{
+			uint y;
+		}
+
+		r = j * 10;
+
+		return;
+	}
+}
+

+ 34 - 0
test/parse2.sol

@@ -0,0 +1,34 @@
+pragma solidity ^0.4.23;
+
+contract foobar {
+	struct foo {
+		int96 x;
+	};
+	struct bar {
+		bytes32 y;
+	};
+	event UpdateActiveAgreementCollectionMap(string name, bytes32 key1, address key2) anonymous;
+      event LogAgreementCreation(
+                bytes32 indexed eventId,
+                address agreement_address,
+                address archetype_address,
+                string name,
+                address creator,
+                bool is_private,
+                uint8   legal_state,
+                uint32 max_event_count,
+                address formation_process_instance,
+                address execution_process_instance,
+                bytes32 hoard_address,
+                bytes32 hoard_secret,
+                bytes32 event_log_hoard_address,
+                bytes32 event_log_hoard_secret
+        );
+
+enum ParameterType {BOOLEAN, STRING, NUMBER, DATE, DATETIME, MONETARY_AMOUNT, USER_ORGANIZATION, CONTRACT_ADDRESS, SIGNING_PARTY};
+	function getId() external view returns (bytes32) {
+		break;
+		continue;
+		102 * 4;
+	}
+}