├── test
├── compiler
├── project.wasp
├── main.w
├── compiler.wasm
├── Makefile
└── index.html
├── wasp
├── src
│ ├── static
│ │ ├── project.wasp
│ │ ├── main.w
│ │ └── index.html
│ └── main.rs
└── Cargo.toml
├── examples
├── simplest
│ ├── main.w
│ ├── simplest.wasm
│ ├── Makefile
│ └── index.html
├── canvas
│ ├── canvas.wasm
│ ├── Makefile
│ ├── index.html
│ └── main.w
├── webgl
│ ├── webgl.wasm
│ ├── Makefile
│ ├── index.html
│ └── main.w
├── testing
│ ├── testing.wasm
│ ├── Makefile
│ ├── main.w
│ └── index.html
├── helloworld
│ ├── main.w
│ ├── Makefile
│ ├── helloworld.wasm
│ └── index.html
└── dynamic_dispatch
│ ├── dynamic_dispatch.wasm
│ ├── Makefile
│ ├── main.w
│ └── index.html
├── .gitignore
├── wasp-core
├── src
│ ├── lib.rs
│ ├── ast.rs
│ ├── parser.rs
│ └── compiler.rs
└── Cargo.toml
├── Makefile
├── LICENSE
├── README.md
└── static_heap.svg
/test:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/compiler/project.wasp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/wasp/src/static/project.wasp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/compiler/main.w:
--------------------------------------------------------------------------------
1 | pub fn main(msg){
2 | 42
3 | }
4 |
--------------------------------------------------------------------------------
/examples/simplest/main.w:
--------------------------------------------------------------------------------
1 | pub fn main(){
2 | 42
3 | }
4 |
--------------------------------------------------------------------------------
/wasp/src/static/main.w:
--------------------------------------------------------------------------------
1 | (defn main "main" []
2 | 42)
3 |
--------------------------------------------------------------------------------
/compiler/compiler.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasplang/wasp/HEAD/compiler/compiler.wasm
--------------------------------------------------------------------------------
/examples/canvas/canvas.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasplang/wasp/HEAD/examples/canvas/canvas.wasm
--------------------------------------------------------------------------------
/examples/webgl/webgl.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasplang/wasp/HEAD/examples/webgl/webgl.wasm
--------------------------------------------------------------------------------
/examples/testing/testing.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasplang/wasp/HEAD/examples/testing/testing.wasm
--------------------------------------------------------------------------------
/examples/helloworld/main.w:
--------------------------------------------------------------------------------
1 | extern console_log(message)
2 | pub fn main(){
3 | console_log("hello world!")
4 | }
5 |
--------------------------------------------------------------------------------
/examples/simplest/simplest.wasm:
--------------------------------------------------------------------------------
1 | asm ` | p
2 | AAmemory main A
3 |
D E@
--------------------------------------------------------------------------------
/examples/canvas/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/examples/simplest/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/examples/testing/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/examples/webgl/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | **/*.rs.bk
3 | Cargo.lock
4 |
5 | /target
6 | **/*.rs.bk
7 | Cargo.lock
8 |
9 | /target
10 | **/*.rs.bk
11 |
--------------------------------------------------------------------------------
/examples/dynamic_dispatch/dynamic_dispatch.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasplang/wasp/HEAD/examples/dynamic_dispatch/dynamic_dispatch.wasm
--------------------------------------------------------------------------------
/examples/helloworld/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/examples/dynamic_dispatch/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../../ && cargo build
3 | ../../target/debug/wasp build
4 | serve:
5 | http-server -p 8080
6 |
--------------------------------------------------------------------------------
/wasp-core/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | extern crate failure;
3 | #[macro_use]
4 | extern crate nom;
5 | pub mod ast;
6 | pub mod compiler;
7 | pub mod parser;
--------------------------------------------------------------------------------
/examples/helloworld/helloworld.wasm:
--------------------------------------------------------------------------------
1 | asm
2 | ` |`||envconsole_log p
3 | AAmemory main A
4 |
D @ A
hello world!
--------------------------------------------------------------------------------
/compiler/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd ../&& cargo build
3 | ../target/debug/wasp build
4 | watch:
5 | while true; do \
6 | make build; \
7 | inotifywait -qre close_write .; \
8 | done
9 | serve:
10 | http-server -p 8080
11 |
--------------------------------------------------------------------------------
/examples/dynamic_dispatch/main.w:
--------------------------------------------------------------------------------
1 | extern console_log(message)
2 | fn heads(){ console_log("heads!") }
3 | fn tails(){ console_log("tails!") }
4 | pub fn main(h){
5 | function_to_call = if(h==1){heads}else{tails}
6 | call(fn()->f64,function_to_call)
7 | }
8 |
--------------------------------------------------------------------------------
/examples/testing/main.w:
--------------------------------------------------------------------------------
1 | pub fn test_multiplication() {
2 | assert(4,(2*2),"2 * 2 should be 4")
3 | assert(1,(1*1),"1 * 1 should be 1")
4 | }
5 |
6 | pub fn test_addition() {
7 | assert(5,(2+2),"2 + 2 should be 4")
8 | assert(2,(1+1),"1 + 1 should be 2")
9 | }
10 |
--------------------------------------------------------------------------------
/examples/simplest/index.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/wasp-core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "wasp-core"
3 | version = "0.0.0"
4 | authors = ["Richard Anaya"]
5 | edition = "2018"
6 | license = "MIT"
7 | description = "Core compiling logic of wasp"
8 |
9 | [dependencies]
10 | failure = "0.1.5"
11 | wasmly = "0.2.0"
12 |
13 | [dependencies.nom]
14 | version = "4"
15 | features = ["verbose-errors"]
--------------------------------------------------------------------------------
/wasp/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "wasp"
3 | version = "0.5.1"
4 | authors = ["Richard Anaya"]
5 | edition = "2018"
6 | license = "MIT"
7 | description = "a web assembly lisp programming language"
8 |
9 | [dependencies]
10 | failure = "0.1.5"
11 | wasmly = "0.2.0"
12 | clap = "2"
13 | walkdir = "2"
14 | wasp-core = {path="../wasp-core",version="0"}
15 |
--------------------------------------------------------------------------------
/examples/canvas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/webgl/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd wasp && cargo build
3 | cd examples/helloworld && make
4 | cd examples/canvas && make
5 | cd examples/testing && make
6 | cd examples/dynamic_dispatch && make
7 | cd examples/simplest && make
8 | cd compiler && make
9 | serve:
10 | http-server -p 8080
11 | publish-std:
12 | cd compiler/vendor/std && git add . && git commit -m 'publishing' && git push
13 | publish:
14 | git add . && git commit -m 'publishing' && git push
15 | cargo publish
16 |
--------------------------------------------------------------------------------
/examples/canvas/main.w:
--------------------------------------------------------------------------------
1 | extern console_log(msg)
2 | extern global_get_window()
3 | extern window_get_document(window)
4 | extern document_query_selector(document,query)
5 | extern htmlcanvas_get_context(element,context)
6 | extern canvas_set_fill_style(canvas,color)
7 | extern canvas_fill_rect(canvas,x,y,w,h)
8 |
9 | static colors = ("black","grey","red")
10 |
11 | pub fn main(){
12 | // setup a drawing context
13 | window = global_get_window()
14 | document = window_get_document(window)
15 | canvas = document_query_selector(document,"#screen")
16 | ctx = htmlcanvas_get_context(canvas,"2d")
17 | x = 0
18 | loop {
19 | // get the offset for the color to use
20 | color_offset = (colors + (x * size_num))
21 | // set current color to string at that position
22 | canvas_set_fill_style(ctx,mem(color_offset))
23 | // draw the rect
24 | canvas_fill_rect(ctx,(x * 10),(x * 10),50,50)
25 | // recur until 3 squares are drawn
26 | x = (x + 1)
27 | if (x < 3) {
28 | recur
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Richard Anaya
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/helloworld/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/wasp/src/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/dynamic_dispatch/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/compiler/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wasp
5 |
6 |
7 |
8 |
9 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/examples/testing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
58 |
59 |
--------------------------------------------------------------------------------
/wasp-core/src/ast.rs:
--------------------------------------------------------------------------------
1 | use wasmly::DataType;
2 |
3 | #[derive(Debug)]
4 | pub struct App {
5 | pub children: Vec,
6 | }
7 |
8 | #[derive(Debug, Clone)]
9 | pub enum TopLevelOperation {
10 | Comment(String),
11 | DefineGlobal(Global),
12 | DefineFunction(FunctionDefinition),
13 | ExternalFunction(ExternalFunction),
14 | }
15 |
16 | #[derive(Debug, Clone)]
17 | pub struct Global {
18 | pub name: String,
19 | pub value: GlobalValue,
20 | }
21 |
22 | #[derive(Debug, Clone)]
23 | pub enum GlobalValue {
24 | Symbol(String),
25 | Number(f64),
26 | Text(String),
27 | Data(Vec),
28 | Identifier(String),
29 | Struct(StructDefinition),
30 | }
31 |
32 | #[derive(Debug, Clone)]
33 | pub struct ExternalFunction {
34 | pub name: String,
35 | pub params: Vec,
36 | }
37 |
38 | #[derive(Debug, Clone)]
39 | pub struct FunctionDefinition {
40 | pub name: String,
41 | pub exported: bool,
42 | pub params: Vec,
43 | pub output: Option,
44 | pub children: Vec,
45 | }
46 |
47 | #[derive(Debug, Clone)]
48 | pub struct StructMember {
49 | pub name: String,
50 | }
51 |
52 | #[derive(Debug, Clone)]
53 | pub struct StructDefinition {
54 | pub members: Vec,
55 | }
56 |
57 | #[derive(Debug, Clone)]
58 | pub struct OperationFunctionCall {
59 | pub function_name: String,
60 | pub params: Vec,
61 | }
62 |
63 | #[derive(Debug, Clone)]
64 | pub struct OperationRecur {}
65 |
66 | #[derive(Debug, Clone)]
67 | pub struct OperationAssignment {
68 | pub id: String,
69 | pub value: Box,
70 | }
71 |
72 | #[derive(Debug, Clone)]
73 | pub struct OperationIfStatement {
74 | pub condition: Box,
75 | pub if_true: Vec,
76 | pub if_false: Option>,
77 | }
78 |
79 | #[derive(Debug, Clone)]
80 | pub struct OperationLoop {
81 | pub expressions: Vec,
82 | }
83 |
84 | #[derive(Debug, Clone)]
85 | pub struct OperationFnSig {
86 | pub inputs: Vec,
87 | pub output: Option,
88 | }
89 |
90 | #[derive(Debug, Clone)]
91 | pub enum Expression {
92 | IfStatement(OperationIfStatement),
93 | Assignment(OperationAssignment),
94 | TextLiteral(String),
95 | SymbolLiteral(String),
96 | Identifier(String),
97 | FunctionCall(OperationFunctionCall),
98 | Number(f64),
99 | Recur(OperationRecur),
100 | Loop(OperationLoop),
101 | FnSig(OperationFnSig),
102 | }
103 |
--------------------------------------------------------------------------------
/examples/webgl/main.w:
--------------------------------------------------------------------------------
1 | (def COLOR_BUFFER_BIT 16384)
2 | (def VERTEX_SHADER 35633)
3 | (def FRAGMENT_SHADER 35632)
4 | (def vertex_shader_src "
5 | precision mediump float;
6 | attribute vec2 vertPosition;
7 | attribute vec3 vertColor;
8 | varying vec3 fragColor;
9 | void main()
10 | {
11 | fragColor = vertColor;
12 | gl_Position = vec4(vertPosition, 0.0, 1.0);
13 | }
14 | ")
15 | (def fragment_shader_src "
16 | precision mediump float;
17 |
18 | varying vec3 fragColor;
19 | void main()
20 | {
21 | gl_FragColor = vec4(fragColor, 1.0);
22 | }
23 | ")
24 |
25 | (extern global_get_window [])
26 | (extern window_get_document [window])
27 | (extern document_query_selector [document query])
28 | (extern htmlcanvas_get_context [element context])
29 | (extern webgl_create_shader [ctx shader_type] )
30 | (extern webgl_shader_source [ctx shader source] )
31 | (extern webgl_compile_shader [ctx shader] )
32 | (extern webgl_create_shader [ctx shader_type vertex_shader_src] )
33 | (extern webgl_create_program [ctx] )
34 | (extern webgl_attach_shader [ctx program shader] )
35 | (extern webgl_link_program [ctx program] )
36 | (extern webgl_use_program [ctx program] )
37 | (extern webgl_clear_color [ctx r g b a] )
38 | (extern webgl_clear [ctx buffer_bit] )
39 | (extern webgl_get_attrib_location [ctx program attrib_name] )
40 |
41 | (defn create_shader [ctx shader_type source]
42 | (let [shader (webgl_create_shader ctx shader_type)]
43 | (webgl_shader_source ctx shader source)
44 | (webgl_compile_shader ctx shader)
45 | shader))
46 |
47 | (defn start_program [ctx]
48 | (let [vertex_shader (create_shader ctx VERTEX_SHADER vertex_shader_src)
49 | fragment_shader (create_shader ctx FRAGMENT_SHADER fragment_shader_src)
50 | program (webgl_create_program ctx)]
51 | (webgl_attach_shader ctx program vertex_shader)
52 | (webgl_attach_shader ctx program fragment_shader)
53 | (webgl_link_program ctx program)
54 | (webgl_use_program ctx program)
55 | program))
56 |
57 | (pub defn main []
58 | (let [win (global_get_window)
59 | doc (window_get_document win)
60 | canvas (document_query_selector doc "#screen")
61 | ctx (htmlcanvas_get_context canvas "webgl")]
62 | (webgl_clear_color ctx 0.75 0.85 0.8 1.0)
63 | (webgl_clear ctx COLOR_BUFFER_BIT)
64 | (let [program (start_program ctx)
65 | position_location (webgl_get_attrib_location ctx program "vertPosition")
66 | color_location (webgl_get_attrib_location ctx program "vertColor")]
67 | 123)))
68 |
69 |
70 | ; // create a program and get its attribute and uniforms
71 | ; let program = start_program(ctx);
72 | ; let position_location = webgl::get_attrib_location(ctx, program, "vertPosition");
73 | ; let color_location = webgl::get_attrib_location(ctx, program, "vertColor");
74 | ; webgl::use_program(ctx, NULL);
75 | ;
76 | ; // setup data buffer
77 | ; let vertices: Vec = vec![
78 | ; // X, Y, R, G, B
79 | ; 0.0, 0.5, 1.0, 1.0, 0.0, -0.5, -0.5, 0.7, 0.0, 1.0, 0.5, -0.5, 0.1, 1.0, 0.6,
80 | ; ];
81 | ; let vertices = create_f32array(&vertices.into_bytes());
82 | ; let vertex_buffer = webgl::create_buffer(ctx);
83 | ; webgl::bind_buffer(ctx, webgl::ARRAY_BUFFER, vertex_buffer);
84 | ; webgl::buffer_data(ctx, webgl::ARRAY_BUFFER, vertices, webgl::STATIC_DRAW);
85 | ; webgl::bind_buffer(ctx, webgl::ARRAY_BUFFER, NULL);
86 | ;
87 | ; // setup for drawing
88 | ; webgl::use_program(ctx, program);
89 | ;
90 | ; // draw
91 | ; webgl::bind_buffer(ctx, webgl::ARRAY_BUFFER, vertex_buffer);
92 | ; webgl::enable_vertex_attrib_array(ctx, position_location);
93 | ; webgl::enable_vertex_attrib_array(ctx, color_location);
94 | ; webgl::vertex_attrib_pointer(
95 | ; ctx,
96 | ; position_location,
97 | ; 2.0,
98 | ; webgl::FLOAT,
99 | ; false,
100 | ; 5.0 * 4.0,
101 | ; 0.0,
102 | ; );
103 | ; webgl::vertex_attrib_pointer(
104 | ; ctx,
105 | ; color_location,
106 | ; 3.0,
107 | ; webgl::FLOAT,
108 | ; false,
109 | ; 5.0 * 4.0,
110 | ; 2.0 * 4.0,
111 | ; );
112 | ; webgl::bind_buffer(ctx, webgl::ARRAY_BUFFER, NULL);
113 | ;
114 | ; webgl::draw_arrays(ctx, webgl::TRIANGLES, 0.0, 3.0);
115 | ; }
116 |
--------------------------------------------------------------------------------
/wasp/src/main.rs:
--------------------------------------------------------------------------------
1 | use failure::Error;
2 | use std::env;
3 | use std::fs::metadata;
4 | use std::fs::File;
5 | use std::io::prelude::*;
6 | use std::str;
7 | extern crate clap;
8 | use clap::{App, AppSettings, Arg, SubCommand};
9 | use std::fs::OpenOptions;
10 | use std::io::{BufRead, BufReader};
11 | use wasp_core::{compiler,parser};
12 |
13 | const VERSION: &'static str = env!("CARGO_PKG_VERSION");
14 |
15 | fn write_output(bytes: &[u8], output_file: Option<&str>) -> std::io::Result<()> {
16 | if output_file.is_none() {
17 | let path = env::current_dir().unwrap();
18 | let output_file = format!(
19 | "{}.wasm",
20 | String::from(path.file_name().unwrap().to_str().unwrap())
21 | );
22 | let mut buffer = File::create(output_file)?;
23 | buffer.write_all(bytes)?;
24 | }
25 | Ok(())
26 | }
27 |
28 |
29 |
30 | fn run(content: &str) -> Result, Error> {
31 | let app = parser::parse(content)?;
32 | compiler::compile(app)
33 | }
34 |
35 | fn main() -> Result<(), Error> {
36 | let matches = App::new("wasp")
37 | .setting(AppSettings::ArgRequiredElseHelp)
38 | .version(VERSION)
39 | .about("A lisp for web assembly")
40 | .author("Richard Anaya")
41 | .subcommand(
42 | SubCommand::with_name("build")
43 | .about("compile a wasp file")
44 | .arg(
45 | Arg::with_name("verbose")
46 | .long("verbose")
47 | .short("v")
48 | .help("Sets the level of verbosity"),
49 | )
50 | .arg(
51 | Arg::with_name("emscripten")
52 | .long("emscripten")
53 | .short("e")
54 | .help("Sets the level of verbosity"),
55 | ),
56 | )
57 | .subcommand(
58 | SubCommand::with_name("init")
59 | .about("initialize a directory to be a wasp project")
60 | .arg(
61 | Arg::with_name("NAME")
62 | .help("Name of project to create folder for")
63 | .required(true),
64 | )
65 | .arg(
66 | Arg::with_name("no-std")
67 | .long("no-std")
68 | .help("don't add the standard library"),
69 | ),
70 | )
71 | .subcommand(SubCommand::with_name("vendor").about("fetch dependencies"))
72 | .subcommand(
73 | SubCommand::with_name("add")
74 | .about("adds a dependency package to this project")
75 | .arg(
76 | Arg::with_name("NAME")
77 | .help("Sets the name of the dependency package")
78 | .required(true),
79 | )
80 | .arg(
81 | Arg::with_name("LOCATION")
82 | .help("A git repo url or folder path")
83 | .required(true),
84 | ),
85 | )
86 | .get_matches();
87 |
88 | if let Some(_matches) = matches.subcommand_matches("build") {
89 | use walkdir::WalkDir;
90 |
91 | let mut files = vec![];
92 | for entry in WalkDir::new("./") {
93 | let entry = entry.unwrap();
94 | let f = entry.path().display().to_string();
95 | if f.ends_with(".w") {
96 | let md = metadata(f.clone()).unwrap();
97 | if md.is_file() {
98 | files.push(f);
99 | }
100 | }
101 | }
102 |
103 | let mut packages = vec![];
104 |
105 | if std::path::Path::new("project.wasp").exists() {
106 | let file = File::open("project.wasp")?;
107 | for line in BufReader::new(file).lines() {
108 | let l = line?;
109 | let v: Vec<&str> = l.split(' ').collect();
110 | packages.push(v[0].to_string())
111 | }
112 | }
113 |
114 | files.sort_by(|a, b| {
115 | if a.starts_with("./vendor/") {
116 | if b.starts_with("./vendor/") {
117 | let sa = a.split('/').collect::>()[2];
118 | let sb = b.split('/').collect::>()[2];
119 | let pa = packages
120 | .iter()
121 | .position(|r| r == sa)
122 | .unwrap_or(std::usize::MAX);
123 | let pb = packages
124 | .iter()
125 | .position(|r| r == sb)
126 | .unwrap_or(std::usize::MAX);
127 | return pa.cmp(&pb);
128 | }
129 | return std::cmp::Ordering::Less;
130 | }
131 | std::cmp::Ordering::Equal
132 | });
133 |
134 | let mut contents = "".to_string();
135 | for file in files {
136 | let c = std::fs::read_to_string(&file).unwrap();
137 | contents = format!("{}\n{}", &contents, &c).to_string();
138 | }
139 |
140 | let output = run(&contents)?;
141 | write_output(&output, None)?;
142 | return Ok(());
143 | };
144 |
145 | if let Some(matches) = matches.subcommand_matches("init") {
146 | let folder = matches.value_of("NAME");
147 | if let Some(f) = folder {
148 | if !std::path::Path::new(&f).exists() {
149 | std::fs::create_dir(f)?;
150 | let mut file = File::create(format!("{}/{}", f, "main.w"))?;
151 | file.write_all(include_bytes!("static/main.w"))?;
152 | let mut file = File::create(format!("{}/{}", f, "project.wasp"))?;
153 | file.write_all(include_bytes!("static/project.wasp"))?;
154 | let mut file = File::create(format!("{}/{}", f, "index.html"))?;
155 | let mut idx = include_str!("static/index.html").to_string();
156 | idx = idx.replace("PROJECT_NAME", &f);
157 | file.write_all((&idx).as_bytes())?;
158 | let no_std = matches.is_present("no-std");
159 | if !no_std {
160 | std::process::Command::new("git")
161 | .args(&[
162 | "clone",
163 | "git@github.com:wasplang/std.git",
164 | &format!("{}/vendor/{}", f, "std"),
165 | ])
166 | .output()
167 | .expect("failed to execute process");
168 | println!("added standard library");
169 | }
170 | println!("created package");
171 | } else {
172 | println!("directory \"{}\" already exists", f);
173 | std::process::exit(1);
174 | }
175 | }
176 | return Ok(());
177 | };
178 |
179 | if let Some(matches) = matches.subcommand_matches("add") {
180 | let name = matches.value_of("NAME").expect("no name");
181 | let location = matches.value_of("LOCATION").expect("no location");
182 | let mut file = OpenOptions::new()
183 | .write(true)
184 | .append(true)
185 | .open("project.wasp")
186 | .unwrap();
187 |
188 | if let Err(e) = writeln!(file, "{} {}", name, location) {
189 | eprintln!("Couldn't write to file: {}", e);
190 | }
191 | std::process::Command::new("git")
192 | .args(&["clone", location, &format!("vendor/{}", name)])
193 | .output()
194 | .expect("failed to execute process");
195 | println!("added dependency");
196 | }
197 |
198 | if matches.subcommand_matches("vendor").is_some() {
199 | std::fs::remove_dir_all("vendor")?;
200 | let file = File::open("project.wasp")?;
201 | for line in BufReader::new(file).lines() {
202 | let l = line?;
203 | let v: Vec<&str> = l.split(' ').collect();
204 | std::process::Command::new("git")
205 | .args(&["clone", v[1], &format!("vendor/{}", v[0])])
206 | .output()
207 | .expect("failed to execute process");
208 | println!("vendoring \"{}\"", v[0]);
209 | }
210 | }
211 |
212 | Ok(())
213 | }
214 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wasp 🐝
2 | a programming language for extremely concise web assembly modules
3 |
4 | **warning:** this compiler is very alpha and error messages aren't the best, but it works and language is simple!
5 |
6 | ```rust
7 | extern console_log(message)
8 |
9 | pub fn main(){
10 | console_log("Hello World!")
11 | }
12 | ```
13 |
14 | # Features
15 | * [x] encourages immutability
16 | * [x] immutable c-strings, memory manipulation, global variables, imported functions, 1st class functions
17 | * [x] optional standard library runtime
18 | * [x] functions with inline web assembly
19 | * [x] test framework support
20 | * [x] easy project dependency management
21 | * [ ] self hosting
22 |
23 | # Quickstart
24 |
25 | Wasp depends on `git` and `rust`. Make sure you have them installed before beginning.
26 |
27 | ```console
28 | cargo install wasp
29 | wasp init myproject
30 | cd myproject
31 | wasp build
32 | python3 -m http.server
33 | ```
34 | Open up http://localhost:8000 and look in console. At this point we will have a web assembly module that has access to the [standard libraries](https://github.com/wasplang/std) functions. More to come in this area!
35 |
36 | If you don't have need for the standard library (or want to write your own!). This is also an option.
37 |
38 | ```console
39 | wasp init myproject --no-std
40 | ```
41 |
42 | At this point we will have a web assembly module with a single exported main function and nothing else.
43 |
44 | If you think your standard library is out of date, just run `wasp vendor`
45 |
46 | # Simple Data Structures
47 |
48 | Wasp is an extremely basic language and standard library.
49 |
50 | ## Linked List
51 |
52 | ```rust
53 | cons(42,nil) // returns the memory location of cons
54 | ```
55 | ```rust
56 | head(cons(42,nil)) // return the head value 42
57 | ```
58 | ```rust
59 | tail(cons(42,nil)) // returns the memory location of tail
60 | ```
61 |
62 | ```rust
63 | cons(1,cons(2,cons(3,nil))) // returns a linked list
64 | ```
65 |
66 | ## Structs
67 |
68 | Structs are dictionaries
69 |
70 | ```rust
71 | struct point { :x :y }
72 |
73 | pub fn create_point(){
74 | foo = malloc(size_of(point))
75 | set(foo,:x,1)
76 | set(foo,:y,1)
77 | foo
78 | }
79 | ```
80 |
81 | # Drawing
82 |
83 | Using [web-dom](https://github.com/web-dom/web-dom) we can easily draw something to screen. Loops in wasp work differently than other languages, observe how this example uses recursion to rebind variables.
84 |
85 | ```rust
86 | extern console_log(msg)
87 | extern global_get_window()
88 | extern window_get_document(window)
89 | extern document_query_selector(document,query)
90 | extern htmlcanvas_get_context(element,context)
91 | extern canvas_set_fill_style(canvas,color)
92 | extern canvas_fill_rect(canvas,x,y,w,h)
93 |
94 | static colors = ("black","grey","red")
95 |
96 | pub fn main(){
97 | // setup a drawing context
98 | window = global_get_window()
99 | document = window_get_document(window)
100 | canvas = document_query_selector(document,"#screen")
101 | ctx = htmlcanvas_get_context(canvas,"2d")
102 |
103 | x = 0
104 | loop {
105 | // get the offset for the color to use
106 | color_offset = (colors + (x * size_num))
107 | // set current color to string at that position
108 | canvas_set_fill_style(ctx,mem(color_offset))
109 | // draw the rect
110 | canvas_fill_rect(ctx,(x * 10),(x * 10),50,50)
111 | // recur until 3 squares are drawn
112 | x = (x + 1)
113 | if (x < 3) {
114 | recur
115 | }
116 | }
117 | }
118 | ```
119 |
120 | See it working [here](https://wasplang.github.io/wasp/examples/canvas/index.html)
121 |
122 | # Mutable Global Data
123 |
124 | It's often important for a web assembly modules to have some sort of global data that can be changed. For instance in a game we might have a high score.
125 |
126 | ```rust
127 | high_score = 0
128 |
129 | fn run_my_game(){
130 | ...
131 | mem(high_score,(mem(high_score) + 100))
132 | ...
133 | }
134 | ```
135 |
136 | # Project Management
137 | **warning: this may change but it works**
138 | Code dependencies are kept in a special folder called `vendor` which is populated by specific checkouts of git repositories.
139 |
140 | For example a `project.wasp` containing:
141 |
142 | ```
143 | bar git@github.com:richardanaya/bar.git@specific-bar
144 | ```
145 |
146 | would result in these commands (roughly)
147 |
148 | ```
149 | rm -rf vendor
150 | mkdir vendor
151 | git clone git@github.com:richardanaya/bar.git@specific-bar vendor/bar
152 | ```
153 |
154 | when `wasp vendor` is called
155 |
156 | Now, when wasp compiles your code, it does a few things.
157 |
158 | * In order specified by your `project.wasp`, one folder at a time all files ending in .w are loaded from each `vendor/` and its subfolders.
159 | * all files in the current directory and sub directories not in `vendor` are loaded
160 | * then everything is compiled in order
161 |
162 | Please try to use non conflicting names in meantime while this is fleshed out.
163 |
164 | # Technical Details
165 | ## Types
166 | It's easiest to think that everything is a `f64` number in wasp.
167 |
168 | * **number** - a 64 bit float
169 | * **string** - a number to a location in memory of the start of of a c-string (e.g. `"hello world!"`)
170 | * **symbol** - a number to a location in memory of the start of of a c-string (e.g. `:hello_world`)
171 | * **bool** - a number representing boolean values. True is 1, false is 0. (e.g. `true` `false`)
172 | * **(...)** - a global only type this is a a number pointer to sequence of values in memory (e.g. `(another_global 1 true :hey (:more-data)`). Use this for embedding raw data into your application memory on startup.
173 |
174 | ## Globals
175 | * **nil** - a number that represents nothingness (0). Note that it is also the same value as false and the number 0.
176 | * **size_num** - the length of a number in bytes (8). This is a global variable in wasp to cut down in magic numbers floating around in code.
177 |
178 | ## Functions
179 | * **[pub] fn name (x,...){ ... })** - create a function that executes a list of expressions returning the result of the last one. Optionally provide an export name to make visible to host.
180 | * **function_name(...)** - call a function with arguments
181 | * **mem_byte(x:integer)** - get 8-bit value from memory location x
182 | * **mem_byte(x:integer y)** - set 8-bit value at memory location x to value y
183 | * **mem(x:integer)** - get 64-bit float value from memory location x
184 | * **mem(x:integer y)** - set 64-bit float value at memory location x to value y
185 |
186 | * **mem_heap_start()** - get number that represents the start of the heap
187 | * **mem_heap_end()** - get number that represents the end of the heap
188 | * **mem_heap_end(x)** - set number value that represents the end of the heap
189 | * **if x { y } )** - if x is true return expression y otherwise return 0
190 | * **if x { y } else { z })** - if x is true return expression y otherwise return expression z
191 | * **x = y** - bind the value of an expression y to an identifier x
192 | * **loop { ... x } ** - executes a list of expressions and returns the last expression x. loop can be restarted with a recur.
193 | * **recur** - restarts a loop
194 | * **fn(x,x1 ..)->y** - gets the value of a function signature with inputs x0, x1, etc and output y
195 | * **call(x,f,y0,y1 ...)** call a function with signature x and function handle f with parameters y0, y1, ...
196 |
197 | ### Common Operators
198 | These oprators work pretty much how you'd expect if you've used C
199 |
200 | * **(x + y)** - sums a list of values and returns result
201 | * **(x - y)** - subtracts a list of values and returns result
202 | * **(x \* y)** - multiplies a list of values and returns result
203 | * **(x / y)** - divides a list of values and returns result
204 | * **(x % y)** - modulos a list of values and returns result
205 | * **(x == y)** - returns true if values are equal, false if otherwise
206 | * **(x != y)** - returns true if values are not equal, false if otherwise
207 | * **(x < y)** - returns true if x is less than y, false if otherwise
208 | * **(x > y)** - returns true if x is greater than y, false if otherwise
209 | * **(x <= y)** - returns true if x is less than or equal y, false if otherwise
210 | * **(x >= y)** - returns true if x is greater than or equal y, false if otherwise
211 | * **(x and y)** - returns true if x and y are true, false if otherwise
212 | * **(x or y)** - returns true if x or y are true, false if otherwise
213 | * **(x & y)** - returns bitwise and of x and y
214 | * **(x | y)** - returns bitwise or of x and y
215 | * **!x** - returns true if zero and false if not zero
216 | * **^x** - bitwise exclusive or of x
217 | * **~x** - bitwise complement of x
218 | * **(x << y)** - shift x left by y bits
219 | * **(x >> y)** - shift x right by y bits
220 |
221 | ## Testing
222 | ```rust
223 | pub test_addition(){
224 | assert(4,(2+2),"2 + 2 should be 4")
225 | assert(7,(3+4),"3 + 4 should be 7")
226 | }
227 | ```
228 | See it working [here](https://wasplang.github.io/wasp/examples/testing/index.html)
229 |
230 | ## Why so few functions?
231 | Wasp prefers to keep as little in the core functionality as possible, letting the [standard library](https://github.com/wasplang/std) evolve faster and more independent community driven manner. This project currently follows a principle that if a feature can be implemented with our primitive functions, don't include it in the core compiled language and let the standard library implement it. Also that no heap based concepts be added to the core language.
232 |
233 | ## Notes
234 |
235 |
236 |
237 |
238 | * all functions (including extern functions) return a value, if no obvious return, it returns ()
239 | * Web assembly global 0 is initialized to the end of the static data section (which might also be the start of a heap for a memory allocator). This value is immutable.
240 | * Web assembly global lobal 1 also is initialized to the end of the static data section. This value is mutable and might be used to represent the end of your heap. Check out the [simple allocator example](https://github.com/richardanaya/wasp/blob/master/examples/malloc/main.w).
241 | * Literal strings create initialize data of a c-string at the front of your memory, and can be passed around as pointers to the very start in memory to your text. A \0 is automatically added at compile time, letting you easily have a marker to denote the end of your text.
242 |
--------------------------------------------------------------------------------
/wasp-core/src/parser.rs:
--------------------------------------------------------------------------------
1 | use crate::ast::*;
2 | use failure::Error;
3 | use nom::types::CompleteStr;
4 | use std::str;
5 | use wasmly::DataType;
6 |
7 | fn to_string(s: CompleteStr) -> String {
8 | s.to_string()
9 | }
10 |
11 | fn is_start_identifier_char(c: char) -> bool {
12 | c == '_' || c == '$' || c.is_alphabetic()
13 | }
14 |
15 | fn is_identifier_char(c: char) -> bool {
16 | c == '_' || c == '!' || c == '-' || c == '$' || c.is_alphanumeric()
17 | }
18 |
19 | fn is_text_char(c: char) -> bool {
20 | c != '"'
21 | }
22 |
23 | fn is_digit(c: char) -> bool {
24 | c.is_digit(10)
25 | }
26 |
27 | fn is_comment_char(c: char) -> bool {
28 | c != '\r' && c != '\n'
29 | }
30 |
31 | fn to_data_type(c: &str) -> DataType {
32 | match c {
33 | "i32" => DataType::I32,
34 | "i64" => DataType::I64,
35 | "f32" => DataType::F32,
36 | "f64" => DataType::F64,
37 | _ => panic!("invalid type"),
38 | }
39 | }
40 |
41 | named!(
42 | token_comment,
43 | do_parse!(
44 | pair: pair!(tag!("//"),take_while!(is_comment_char))>>
45 | (pair.0.to_string())
46 | )
47 | );
48 |
49 | named!(
50 | token_identifier,
51 | do_parse!(
52 | start: map!(take_while1!(is_start_identifier_char), to_string) >>
53 | end: map!(take_while!(is_identifier_char), to_string) >>
54 | (format!("{}{}",start,end).to_string())
55 | )
56 | );
57 |
58 | named!(
59 | operator_identifiers,
60 | do_parse!(
61 | id: alt!(map!(tag!(">>"),to_string)|map!(tag!("<<"),to_string)|map!(tag!(">="),to_string)|map!(tag!("<="),to_string)|map!(tag!(">"),to_string)|map!(tag!("<"),to_string)|map!(tag!("or"),to_string)|map!(tag!("and"),to_string)|map!(tag!("!="),to_string)|map!(tag!("=="),to_string)|map!(tag!("+"),to_string)|map!(tag!("-"),to_string)|map!(tag!("*"),to_string)|map!(tag!("/"),to_string)|map!(tag!("%"),to_string)|map!(tag!("|"),to_string)|map!(tag!("&"),to_string))>>
62 | (id)
63 | )
64 | );
65 |
66 | named!(
67 | unary_operator_identifiers,
68 | do_parse!(
69 | id: alt!(map!(tag!("^"),to_string)|map!(tag!("~"),to_string)|map!(tag!("!"),to_string))>>
70 | (id)
71 | )
72 | );
73 |
74 | named!(
75 | function_identifiers,
76 | do_parse!(
77 | id: alt!(map!(tag!("assert"),to_string)|map!(tag!("call"),to_string)|token_identifier)>>
78 | (id)
79 | )
80 | );
81 |
82 | named!(
83 | token_data_type,
84 | do_parse!(
85 | t: map!(alt!(tag!("()")|tag!("i32")|tag!("i64")|tag!("f32")|tag!("f64")), to_string) >>
86 | (to_data_type(&t))
87 | )
88 | );
89 |
90 | named!(
91 | token_text,
92 | do_parse!(
93 | tag!("\"")
94 | >> text: map!(take_while!(is_text_char), to_string)
95 | >> tag!("\"")
96 | >> (text)
97 | )
98 | );
99 |
100 | named!(
101 | token_symbol,
102 | do_parse!(
103 | tag!(":")
104 | >> text: map!(take_while!(is_identifier_char), to_string)
105 | >> (text)
106 | )
107 | );
108 |
109 | named!(
110 | base_float,
111 | do_parse!(
112 | num: map!(take_while1!(is_digit), to_string) >>
113 | tag!(".") >>
114 | den: map!(take_while1!(is_digit), to_string) >>
115 | (format!("{}.{}",num,den).to_owned())
116 | )
117 | );
118 |
119 | named!(
120 | base_int,
121 | do_parse!(
122 | num: map!(take_while1!(is_digit), to_string) >>
123 | (num.to_owned())
124 | )
125 | );
126 |
127 | named!(
128 | negative_number,
129 | do_parse!(
130 | tag!("-")
131 | >> num: alt!(base_float|base_int)
132 | >> (-num.parse::().unwrap())
133 | )
134 | );
135 |
136 | named!(
137 | positive_number,
138 | do_parse!(
139 | num: alt!(base_float|base_int)
140 | >> (num.parse::().unwrap())
141 | )
142 | );
143 |
144 | named!(
145 | token_number,
146 | alt!(positive_number|negative_number)
147 | );
148 |
149 | named!(external_function,
150 | do_parse!(
151 | ws!(tag!("extern")) >>
152 | function_name: ws!(token_identifier) >>
153 | ws!(tag!("(")) >>
154 | params: ws!(separated_list!(tag!(","),ws!(token_identifier))) >>
155 | ws!(tag!(")")) >>
156 | (TopLevelOperation::ExternalFunction(ExternalFunction{name:function_name,params:params}))
157 | )
158 | );
159 |
160 | named!(expression_literal_string,
161 | do_parse!(
162 | text: ws!(token_text) >>
163 | (Expression::TextLiteral(text))
164 | )
165 | );
166 |
167 | named!(expression_literal_token,
168 | do_parse!(
169 | text: ws!(token_symbol) >>
170 | (Expression::SymbolLiteral(text))
171 | )
172 | );
173 |
174 | named!(expression_identifier,
175 | do_parse!(
176 | text: ws!(token_identifier) >>
177 | (Expression::Identifier(text))
178 | )
179 | );
180 |
181 | named!(expression_number,
182 | do_parse!(
183 | num: ws!(token_number) >>
184 | (Expression::Number(num))
185 | )
186 | );
187 |
188 | named!(boolean_true,
189 | do_parse!(
190 | tag!("true") >>
191 | (Expression::Number(1.0))
192 | )
193 | );
194 |
195 | named!(boolean_false,
196 | do_parse!(
197 | tag!("false") >>
198 | (Expression::Number(0.0))
199 | )
200 | );
201 |
202 | named!(expression_let_pair,
203 | do_parse!(
204 | id: ws!(token_identifier) >>
205 | exp: ws!(expression) >>
206 | ((id,exp))
207 | )
208 | );
209 |
210 | named!(expression_loop,
211 | do_parse!(
212 | ws!(tag!("loop")) >>
213 | many0!(ws!(token_comment)) >>
214 | ws!(tag!("{")) >>
215 | expressions: expression_list >>
216 | tag!("}") >>
217 | (Expression::Loop(OperationLoop{expressions:expressions}))
218 | )
219 | );
220 |
221 | named!(expression_recur,
222 | do_parse!(
223 | tag!("recur") >>
224 | (Expression::Recur(OperationRecur{}))
225 | )
226 | );
227 |
228 | named!(expression_fnsig,
229 | do_parse!(
230 | ws!(tag!("fn")) >>
231 | many0!(ws!(token_comment)) >>
232 | ws!(tag!("(")) >>
233 | many0!(ws!(token_comment)) >>
234 | inputs: ws!(separated_list!(tag!(","),ws!(token_data_type))) >>
235 | many0!(ws!(token_comment)) >>
236 | ws!(tag!(")")) >>
237 | ws!(tag!("->")) >>
238 | many0!(ws!(token_comment)) >>
239 | output: opt!(ws!(token_data_type)) >>
240 | (Expression::FnSig(OperationFnSig{inputs:inputs, output:output}))
241 | )
242 | );
243 |
244 | named!(expression,
245 | alt!(expression_if_statement|expression_fnsig|expression_operator_call|expression_unary_operator_call|expression_assignment|expression_function_call|expression_loop|expression_recur|expression_number|boolean_true|boolean_false|expression_literal_token|expression_literal_string|expression_identifier)
246 | );
247 |
248 | named!(expression_list_item,
249 | do_parse!(
250 | many0!(ws!(token_comment)) >>
251 | expr: ws!(expression) >>
252 | (expr)
253 | )
254 | );
255 |
256 | named!(expression_list>,
257 | do_parse!(
258 | exprs: ws!(ws!(many1!(ws!(expression_list_item)))) >>
259 | (exprs)
260 | )
261 | );
262 |
263 | named!(function_params>,
264 | do_parse!(
265 | op: ws!(separated_list!(tag!(","),ws!(expression))) >>
266 | (op)
267 | )
268 | );
269 |
270 | named!(expression_operator_call,
271 | do_parse!(
272 | tag!("(") >>
273 | expr_a: ws!(expression) >>
274 | function_name: ws!(operator_identifiers) >>
275 | expr_b: ws!(expression) >>
276 | tag!(")") >>
277 | (Expression::FunctionCall(OperationFunctionCall{function_name:function_name,params:vec![expr_a,expr_b]}))
278 | )
279 | );
280 |
281 | named!(expression_assignment,
282 | do_parse!(
283 | id: ws!(token_identifier) >>
284 | ws!(tag!("=")) >>
285 | expr: ws!(expression) >>
286 | (Expression::Assignment(OperationAssignment{id:id,value:Box::new(expr)}))
287 | )
288 | );
289 |
290 | named!(expression_else_statement>,
291 | do_parse!(
292 | ws!(tag!("else")) >>
293 | ws!(tag!("{")) >>
294 | expr_c: expression_list >>
295 | tag!("}") >>
296 | (expr_c)
297 | )
298 | );
299 |
300 | named!(expression_if_statement,
301 | do_parse!(
302 | ws!(tag!("if")) >>
303 | expr_a: ws!(expression) >>
304 | ws!(tag!("{")) >>
305 | expr_b: expression_list >>
306 | tag!("}") >>
307 | expr_c: ws!(opt!(expression_else_statement)) >>
308 | (Expression::IfStatement(OperationIfStatement{condition:Box::new(expr_a),if_true:expr_b,if_false:expr_c}))
309 | )
310 | );
311 |
312 | named!(expression_unary_operator_call,
313 | do_parse!(
314 | function_name: ws!(unary_operator_identifiers) >>
315 | expr_a: ws!(expression) >>
316 | (Expression::FunctionCall(OperationFunctionCall{function_name:function_name,params:vec![expr_a]}))
317 | )
318 | );
319 |
320 | named!(expression_function_call,
321 | do_parse!(
322 | function_name: ws!(function_identifiers) >>
323 | tag!("(") >>
324 | params: ws!(function_params) >>
325 | ws!(tag!(")")) >>
326 | (Expression::FunctionCall(OperationFunctionCall{function_name:function_name,params:params}))
327 | )
328 | );
329 |
330 | named!(define_function,
331 | do_parse!(
332 | external_name:opt!( ws!(tag!("pub"))) >>
333 | many0!(ws!(token_comment)) >>
334 | ws!(tag!("fn")) >>
335 | many0!(ws!(token_comment)) >>
336 | function_name: ws!(token_identifier) >>
337 | many0!(ws!(token_comment)) >>
338 | ws!(tag!("(")) >>
339 | many0!(ws!(token_comment)) >>
340 | params: ws!(separated_list!(tag!(","),ws!(token_identifier))) >>
341 | many0!(ws!(token_comment)) >>
342 | ws!(tag!(")")) >>
343 | many0!(ws!(token_comment)) >>
344 | ws!(tag!("{")) >>
345 | children: expression_list >>
346 | tag!("}") >>
347 | (TopLevelOperation::DefineFunction(FunctionDefinition{name: function_name,
348 | exported: external_name.is_some(),
349 | params: params,
350 | output: None,
351 | children: children}))
352 | )
353 | );
354 |
355 | named!(struct_pair,
356 | do_parse!(
357 | name: token_symbol >>
358 | many0!(ws!(token_comment)) >>
359 | (StructMember{name: name})
360 | )
361 | );
362 |
363 | named!(define_struct,
364 | do_parse!(
365 | ws!(tag!("struct")) >>
366 | many0!(ws!(token_comment)) >>
367 | name: ws!(token_identifier) >>
368 | many0!(ws!(token_comment)) >>
369 | tag!("{") >>
370 | many0!(ws!(token_comment)) >>
371 | members: many0!(ws!(struct_pair)) >>
372 | many0!(ws!(token_comment)) >>
373 | tag!("}") >>
374 | (TopLevelOperation::DefineGlobal(Global{name:name,value:GlobalValue::Struct(StructDefinition{
375 | members: members})}))
376 | )
377 | );
378 |
379 | named!(value_number,
380 | do_parse!(
381 | value: token_number >>
382 | (GlobalValue::Number(value))
383 | )
384 | );
385 |
386 | named!(value_text,
387 | do_parse!(
388 | value: token_text >>
389 | (GlobalValue::Text(value))
390 | )
391 | );
392 |
393 | named!(value_symbol,
394 | do_parse!(
395 | value: token_symbol >>
396 | (GlobalValue::Symbol(value))
397 | )
398 | );
399 |
400 | named!(global_bool_true,
401 | do_parse!(
402 | tag!("true") >>
403 | (GlobalValue::Number(1.0))
404 | )
405 | );
406 |
407 | named!(global_bool_false,
408 | do_parse!(
409 | tag!("false") >>
410 | (GlobalValue::Number(0.0))
411 | )
412 | );
413 |
414 | named!(global_identifier,
415 | do_parse!(
416 | value: token_identifier >>
417 | (GlobalValue::Identifier(value))
418 | )
419 | );
420 |
421 | named!(global_data,
422 | do_parse!(
423 | tag!("(") >>
424 | values: ws!(separated_list!(tag!(","),ws!(alt!(global_value|global_identifier)))) >>
425 | tag!(")") >>
426 | (GlobalValue::Data(values))
427 | )
428 | );
429 |
430 | named!(global_value,
431 | do_parse!(
432 | value: ws!(alt!(global_bool_true|global_bool_false|value_number|value_symbol|value_text|global_data)) >>
433 | (value)
434 | )
435 | );
436 |
437 | named!(define_global,
438 | do_parse!(
439 | ws!(tag!("static")) >>
440 | name: ws!(token_identifier) >>
441 | ws!(tag!("=")) >>
442 | value: global_value >>
443 | (TopLevelOperation::DefineGlobal(Global{name: name,value:value}))
444 | )
445 | );
446 |
447 | named!(comment,
448 | do_parse!(
449 | tag!("//") >>
450 | comment: map!(take_while!(is_comment_char),to_string) >>
451 | (TopLevelOperation::Comment(comment))
452 | )
453 | );
454 |
455 | named!(app,
456 | do_parse!(
457 | op: many0!(ws!(alt!(comment|external_function|define_function|define_struct|define_global))) >>
458 | eof!() >>
459 | (App{children:op})
460 | )
461 | );
462 |
463 | pub fn parse(content: &str) -> Result {
464 | let result = app(CompleteStr(content));
465 | match result {
466 | Ok((_, value)) => Ok(value),
467 | Err(nom::Err::Incomplete(needed)) => Err(format_err!("{:?}", needed)),
468 | Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(format_err!("{:?}", e)),
469 | }
470 | }
471 |
--------------------------------------------------------------------------------
/static_heap.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/wasp-core/src/compiler.rs:
--------------------------------------------------------------------------------
1 | use crate::ast::*;
2 | use failure::Error;
3 | use wasmly::WebAssembly::*;
4 | use wasmly::*;
5 |
6 | #[derive(PartialEq)]
7 | enum IdentifierType {
8 | Global,
9 | Local,
10 | Function,
11 | }
12 |
13 | struct Compiler {
14 | wasm: wasmly::App,
15 | ast: crate::ast::App,
16 | symbols: Vec,
17 | global_names: Vec,
18 | global_values: Vec,
19 | local_names: Vec,
20 | heap_position: f64,
21 | function_defs: Vec,
22 | function_names: Vec,
23 | function_implementations: Vec,
24 | non_imported_functions: Vec,
25 | recur_depth: u32,
26 | return_depth: u32,
27 | }
28 |
29 | impl Compiler {
30 | fn new(app: crate::ast::App) -> Compiler {
31 | let mut c = Compiler {
32 | wasm: wasmly::App::new(vec![]),
33 | ast: app,
34 | symbols: vec![],
35 | global_names: vec![],
36 | global_values: vec![],
37 | local_names: vec![],
38 | heap_position: 4.0, //start at 4 so nothing has 0 address
39 | function_defs: vec![],
40 | function_names: vec![],
41 | function_implementations: vec![],
42 | non_imported_functions: vec![],
43 | recur_depth: 0,
44 | return_depth: 1,
45 | };
46 | c.initialize();
47 | c
48 | }
49 |
50 | fn initialize(&mut self) {
51 | //Get imports so we can start creating app
52 | let import_defs = self
53 | .ast
54 | .children
55 | .iter()
56 | .filter_map(|x| match x {
57 | TopLevelOperation::ExternalFunction(x) => Some(x),
58 | _ => None,
59 | })
60 | .collect::>();
61 |
62 | let mut imports = vec![];
63 | for def in import_defs {
64 | self.function_names.push(def.name.clone());
65 | imports.push(Import::ImportFunction(ImportFunction::new(
66 | def.name.clone(),
67 | def.params.iter().map(|_| DataType::F64).collect(),
68 | Some(DataType::F64),
69 | )))
70 | }
71 | self.wasm = wasmly::App::new(imports);
72 | self.function_defs = self
73 | .ast
74 | .children
75 | .iter()
76 | .filter_map(|x| match x {
77 | TopLevelOperation::DefineFunction(_) => Some(x.clone()),
78 | _ => None,
79 | })
80 | .collect::>();
81 | }
82 |
83 | fn process_globals(&mut self) {
84 | let global_defs = self
85 | .ast
86 | .children
87 | .iter()
88 | .filter_map(|x| match x {
89 | TopLevelOperation::DefineGlobal(x) => Some(x.clone()),
90 | _ => None,
91 | })
92 | .collect::>();
93 | for def in global_defs {
94 | self.global_names.push(def.name.clone());
95 | let v = self.get_global_value(&def.value);
96 | self.global_values.push(v);
97 | }
98 | }
99 |
100 | fn float_to_bytes(&self, i: f64) -> Vec {
101 | let raw_bytes: [u8; 8] = unsafe { std::mem::transmute(i) };
102 | let bytes: Vec = raw_bytes.to_vec();
103 | bytes
104 | }
105 |
106 | fn create_global_data(&mut self, v: Vec) -> f64 {
107 | let mut bytes = vec![];
108 | for i in 0..v.len() {
109 | let v = self.get_global_value(&v[i]);
110 | let b = self.float_to_bytes(v);
111 | bytes.extend_from_slice(&b);
112 | }
113 | self.create_data(bytes)
114 | }
115 |
116 | fn get_symbol_value(&mut self, t: &str) -> f64 {
117 | // no symbol has the value 0
118 | let v = self.symbols.iter().enumerate().find(|x| &x.1 == &t);
119 | if let Some(i) = v {
120 | return i.0 as f64 + 1.0;
121 | } else {
122 | self.symbols.push(t.to_string());
123 | return self.symbols.len() as f64;
124 | }
125 | }
126 |
127 | fn get_global_value(&mut self, v: &GlobalValue) -> f64 {
128 | match v {
129 | GlobalValue::Symbol(t) => self.get_symbol_value(t),
130 | GlobalValue::Number(t) => *t,
131 | GlobalValue::Text(t) => self.get_or_create_text_data(&t),
132 | GlobalValue::Data(t) => self.create_global_data(t.clone()),
133 | GlobalValue::Struct(s) => {
134 | let mut t: Vec = vec![];
135 | for i in 0..s.members.len() {
136 | t.push(GlobalValue::Symbol(s.members[i].name.clone()));
137 | }
138 | t.push(GlobalValue::Number(0.0));
139 | self.create_global_data(t)
140 | }
141 | GlobalValue::Identifier(t) => {
142 | self.resolve_identifier(t)
143 | .expect(&format!("{} is not a valid identifier", &t))
144 | .0
145 | }
146 | }
147 | }
148 |
149 | fn pre_process_functions(&mut self) {
150 | // gather all the function names and positions we shall use
151 | self.non_imported_functions = vec![];
152 | for i in 0..self.function_defs.len() {
153 | if let TopLevelOperation::DefineFunction(function_def) = &self.function_defs[i] {
154 | self.function_names.push(function_def.name.clone());
155 | self.non_imported_functions.push(function_def.name.clone());
156 | }
157 | }
158 |
159 | // get the basics about our functions loaded into memory
160 | for i in 0..self.function_defs.len() {
161 | if let TopLevelOperation::DefineFunction(function_def) = &self.function_defs[i] {
162 | let mut function = Function::new();
163 | if function_def.exported {
164 | function.with_name(&function_def.name);
165 | }
166 | function.with_inputs(function_def.params.iter().map(|_| DataType::F64).collect());
167 | function.with_output(DataType::F64);
168 | self.function_implementations.push(function);
169 | }
170 | }
171 |
172 | self.wasm.add_table(wasmly::Table::new(
173 | self.function_names.len() as u32,
174 | self.function_names.len() as u32,
175 | ));
176 | }
177 |
178 | fn set_heap_start(&mut self) {
179 | //set global heap once we know what it should be
180 | let final_heap_pos = {
181 | if self.heap_position % 4.0 != 0.0 {
182 | (self.heap_position / 4.0) * 4.0 + 4.0
183 | } else {
184 | self.heap_position
185 | }
186 | };
187 | self.wasm
188 | .add_global(wasmly::Global::new(final_heap_pos as i32, false));
189 | self.wasm
190 | .add_global(wasmly::Global::new(final_heap_pos as i32, true));
191 | }
192 |
193 | fn get_or_create_text_data(&mut self, str: &str) -> f64 {
194 | let mut bytes: Vec = str.as_bytes().into();
195 | bytes.push(0);
196 | self.create_data(bytes)
197 | }
198 |
199 | fn create_data(&mut self, bytes: Vec) -> f64 {
200 | let pos = self.heap_position;
201 | let size = bytes.len();
202 | self.wasm.add_data(Data::new(pos as i32, bytes));
203 | let mut final_heap_pos = self.heap_position + (size as f64);
204 | // align data to 4
205 | // TODO: verify if this actually matters
206 | if final_heap_pos % 4.0 != 0.0 {
207 | final_heap_pos = (final_heap_pos / 4.0) * 4.0 + 4.0;
208 | }
209 | self.heap_position = final_heap_pos;
210 | pos
211 | }
212 |
213 | fn resolve_identifier(&self, id: &str) -> Option<(f64, IdentifierType)> {
214 | if id == "nil" {
215 | return Some((0.0, IdentifierType::Global));
216 | }
217 | if id == "size_num" {
218 | return Some((8.0, IdentifierType::Global));
219 | }
220 | // look this up in reverse so shadowing works
221 | let mut p = self.local_names.iter().rev().position(|r| r == id);
222 | if p.is_some() {
223 | return Some((
224 | self.local_names.len() as f64 - 1.0 - p.unwrap() as f64,
225 | IdentifierType::Local,
226 | ));
227 | }
228 | p = self.function_names.iter().position(|r| r == id);
229 | if p.is_some() {
230 | return Some((p.unwrap() as f64, IdentifierType::Function));
231 | }
232 | p = self.global_names.iter().position(|r| r == id);
233 | if p.is_some() {
234 | return Some((self.global_values[p.unwrap()], IdentifierType::Global));
235 | }
236 | None
237 | }
238 |
239 | #[allow(clippy::cyclomatic_complexity)]
240 | fn process_expression(&mut self, i: usize, e: &Expression) {
241 | match e {
242 | Expression::SymbolLiteral(x) => {
243 | let v = self.get_symbol_value(x);
244 | self.function_implementations[i].with_instructions(vec![F64_CONST, v.into()]);
245 | }
246 | Expression::FnSig(x) => {
247 | let t = self
248 | .wasm
249 | .add_type(FunctionType::new(x.inputs.clone(), x.output.clone()));
250 | self.function_implementations[i]
251 | .with_instructions(vec![F64_CONST, (t as f64).into()]);
252 | }
253 | Expression::Loop(x) => {
254 | self.recur_depth = 0;
255 | if !x.expressions.is_empty() {
256 | self.function_implementations[i].with_instructions(vec![LOOP, F64]);
257 | for k in 0..x.expressions.len() {
258 | self.process_expression(i, &x.expressions[k]);
259 | if k != x.expressions.len() - 1 {
260 | self.function_implementations[i].with_instructions(vec![DROP]);
261 | }
262 | }
263 | self.function_implementations[i].with_instructions(vec![END]);
264 | } else {
265 | panic!("useless infinite loop detected")
266 | }
267 | }
268 | Expression::Recur(_) => {
269 | self.function_implementations[i].with_instructions(vec![
270 | F64_CONST,
271 | 0.0.into(),
272 | BR,
273 | self.recur_depth.into(),
274 | ]);
275 | }
276 | Expression::IfStatement(x) => {
277 | self.recur_depth += 1;
278 | self.process_expression(i, &x.condition);
279 | self.function_implementations[i].with_instructions(vec![
280 | F64_CONST,
281 | 0.0.into(),
282 | F64_EQ,
283 | I32_CONST,
284 | 0.into(),
285 | I32_EQ,
286 | ]);
287 | self.function_implementations[i].with_instructions(vec![IF, F64]);
288 | for k in 0..x.if_true.len() {
289 | self.process_expression(i, &x.if_true[k]);
290 | if k != x.if_true.len() - 1 {
291 | self.function_implementations[i].with_instructions(vec![DROP]);
292 | }
293 | }
294 | self.function_implementations[i].with_instructions(vec![ELSE]);
295 | if x.if_false.is_some() {
296 | for k in 0..x.if_false.as_ref().unwrap().len() {
297 | self.process_expression(i, &x.if_false.as_ref().unwrap()[k]);
298 | if k != x.if_false.as_ref().unwrap().len() - 1 {
299 | self.function_implementations[i].with_instructions(vec![DROP]);
300 | }
301 | }
302 | } else {
303 | self.function_implementations[i].with_instructions(vec![F64_CONST, 0.0.into()]);
304 | }
305 | self.function_implementations[i].with_instructions(vec![END]);
306 | }
307 | Expression::Assignment(x) => {
308 | self.process_expression(i, &x.value);
309 | self.function_implementations[i].with_local(DataType::F64);
310 | let p = self.resolve_identifier(&x.id);
311 | let idx = if p.is_some() {
312 | let ident = p.unwrap();
313 | if ident.1 == IdentifierType::Local {
314 | ident.0 as u32
315 | } else {
316 | let l = self.local_names.len() as u32;
317 | self.local_names.push((&x.id).to_string());
318 | l
319 | }
320 | } else {
321 | let l = self.local_names.len() as u32;
322 | self.local_names.push((&x.id).to_string());
323 | l
324 | };
325 | self.function_implementations[i].with_instructions(vec![
326 | LOCAL_SET,
327 | idx.into(),
328 | LOCAL_GET,
329 | idx.into(),
330 | ]);
331 | }
332 | Expression::FunctionCall(x) => {
333 | if &x.function_name == "assert" {
334 | if x.params.len() == 3 {
335 | self.process_expression(i, &x.params[0]);
336 | self.process_expression(i, &x.params[1]);
337 | self.function_implementations[i].with_instructions(vec![F64_EQ]);
338 | self.function_implementations[i].with_instructions(vec![IF, F64]);
339 | self.function_implementations[i]
340 | .with_instructions(vec![F64_CONST, 0.0.into()]);
341 | self.function_implementations[i].with_instructions(vec![ELSE]);
342 | self.process_expression(i, &x.params[2]);
343 | self.function_implementations[i].with_instructions(vec![
344 | BR,
345 | self.return_depth.into(),
346 | END,
347 | ]);
348 | } else {
349 | panic!("assert has 3 parameters")
350 | }
351 | } else if &x.function_name == "call" {
352 | if x.params.len() >= 2 {
353 | if let Expression::FnSig(sig) = &x.params[0] {
354 | for k in 2..x.params.len() {
355 | self.process_expression(i, &x.params[k]);
356 | }
357 | self.process_expression(i, &x.params[1]);
358 | self.function_implementations[i]
359 | .with_instructions(vec![I32_TRUNC_S_F64]);
360 | let t = self.wasm.add_type(FunctionType::new(
361 | sig.inputs.clone(),
362 | sig.output.clone(),
363 | ));
364 | self.function_implementations[i].with_instructions(vec![
365 | CALL_INDIRECT,
366 | t.into(),
367 | 0.into(),
368 | ]);
369 | if sig.output.is_none() {
370 | self.function_implementations[i]
371 | .with_instructions(vec![F64_CONST, 0.0.into()]);
372 | }
373 | } else {
374 | panic!("call must begin with a function signature not an expression")
375 | }
376 | } else {
377 | panic!("call must have at least function signature and function index")
378 | }
379 | } else if &x.function_name == "mem_byte" {
380 | if x.params.len() == 1 {
381 | self.process_expression(i, &x.params[0]);
382 | self.function_implementations[i].with_instructions(vec![I32_TRUNC_S_F64]);
383 | self.function_implementations[i].with_instructions(vec![
384 | I32_LOAD8_U,
385 | 0.into(),
386 | 0.into(),
387 | F64_CONVERT_S_I32,
388 | ]);
389 | } else if x.params.len() == 2 {
390 | for k in 0..x.params.len() {
391 | self.process_expression(i, &x.params[k]);
392 | self.function_implementations[i]
393 | .with_instructions(vec![I32_TRUNC_S_F64]);
394 | }
395 | self.function_implementations[i].with_instructions(vec![
396 | I32_STORE8,
397 | 0.into(),
398 | 0.into(),
399 | ]);
400 | self.function_implementations[i]
401 | .with_instructions(vec![F64_CONST, 0.0.into()]);
402 | } else {
403 | panic!("invalid number params for mem_byte")
404 | }
405 | } else if &x.function_name == "mem_heap_start" {
406 | if x.params.len() == 0 {
407 | self.function_implementations[i].with_instructions(vec![
408 | GLOBAL_GET,
409 | 0.into(),
410 | F64_CONVERT_S_I32,
411 | ]);
412 | } else {
413 | panic!("invalid number params for mem_heap_start")
414 | }
415 | } else if &x.function_name == "mem_heap_end" {
416 | if x.params.len() == 0 {
417 | self.function_implementations[i].with_instructions(vec![
418 | GLOBAL_GET,
419 | 1.into(),
420 | F64_CONVERT_S_I32,
421 | ]);
422 | } else if x.params.len() == 1 {
423 | self.process_expression(i, &x.params[0]);
424 | self.function_implementations[i].with_instructions(vec![I32_TRUNC_S_F64]);
425 | self.function_implementations[i].with_instructions(vec![
426 | GLOBAL_SET,
427 | 1.into(),
428 | I32_CONST,
429 | 0.into(),
430 | ]);
431 | } else {
432 | panic!("invalid number params for mem_heap_start")
433 | }
434 | } else if &x.function_name == "mem" {
435 | if x.params.len() == 1 {
436 | self.process_expression(i, &x.params[0]);
437 | self.function_implementations[i].with_instructions(vec![
438 | I32_TRUNC_S_F64,
439 | F64_LOAD,
440 | (0 as i32).into(),
441 | (0 as i32).into(),
442 | ]);
443 | } else if x.params.len() == 2 {
444 | self.process_expression(i, &x.params[0]);
445 | self.function_implementations[i].with_instructions(vec![I32_TRUNC_S_F64]);
446 | self.process_expression(i, &x.params[1]);
447 | self.function_implementations[i].with_instructions(vec![
448 | F64_STORE,
449 | (0 as i32).into(),
450 | (0 as i32).into(),
451 | ]);
452 | self.function_implementations[i]
453 | .with_instructions(vec![F64_CONST, 0.0.into()]);
454 | } else {
455 | panic!("invalid number params for mem")
456 | }
457 | } else if &x.function_name == "=="
458 | || &x.function_name == "!="
459 | || &x.function_name == "<="
460 | || &x.function_name == ">="
461 | || &x.function_name == "<"
462 | || &x.function_name == ">"
463 | {
464 | if x.params.len() != 2 {
465 | panic!(
466 | "operator {} expected 2 parameters",
467 | (&x.function_name).as_str()
468 | );
469 | }
470 | self.process_expression(i, &x.params[0]);
471 | self.process_expression(i, &x.params[1]);
472 | let mut f = match (&x.function_name).as_str() {
473 | "==" => vec![F64_EQ],
474 | "!=" => vec![F64_NE],
475 | "<=" => vec![F64_LE],
476 | ">=" => vec![F64_GE],
477 | "<" => vec![F64_LT],
478 | ">" => vec![F64_GT],
479 | _ => panic!("unexpected operator"),
480 | };
481 | f.extend(vec![F64_CONVERT_S_I32]);
482 | self.function_implementations[i].with_instructions(f);
483 | } else if &x.function_name == "&"
484 | || &x.function_name == "|"
485 | || &x.function_name == "^"
486 | || &x.function_name == "<<"
487 | || &x.function_name == ">>"
488 | {
489 | if x.params.len() != 2 {
490 | panic!(
491 | "operator {} expected 2 parameters",
492 | (&x.function_name).as_str()
493 | );
494 | }
495 | self.process_expression(i, &x.params[0]);
496 | self.function_implementations[i].with_instructions(vec![I64_TRUNC_S_F64]);
497 | self.process_expression(i, &x.params[1]);
498 | self.function_implementations[i].with_instructions(vec![I64_TRUNC_S_F64]);
499 | let mut f = match (&x.function_name).as_str() {
500 | "&" => vec![I64_AND],
501 | "|" => vec![I64_OR],
502 | "^" => vec![I64_XOR],
503 | "<<" => vec![I64_SHL],
504 | ">>" => vec![I64_SHR_S],
505 | _ => panic!("unexpected operator"),
506 | };
507 | f.extend(vec![F64_CONVERT_S_I64]);
508 | self.function_implementations[i].with_instructions(f);
509 | } else if &x.function_name == "+"
510 | || &x.function_name == "-"
511 | || &x.function_name == "*"
512 | || &x.function_name == "/"
513 | || &x.function_name == "%"
514 | {
515 | if x.params.len() < 2 {
516 | panic!(
517 | "operator {} expected at least 2 parameters",
518 | (&x.function_name).as_str()
519 | );
520 | }
521 | for p in 0..x.params.len() {
522 | self.process_expression(i, &x.params[p]);
523 |
524 | if &x.function_name == "%" {
525 | self.function_implementations[i]
526 | .with_instructions(vec![I64_TRUNC_S_F64]);
527 | }
528 | if p != 0 {
529 | let f = match (&x.function_name).as_str() {
530 | "+" => vec![F64_ADD],
531 | "-" => vec![F64_SUB],
532 | "*" => vec![F64_MUL],
533 | "/" => vec![F64_DIV],
534 | "%" => vec![I64_REM_S, F64_CONVERT_S_I64],
535 | _ => panic!("unexpected operator"),
536 | };
537 | self.function_implementations[i].with_instructions(f);
538 | }
539 | }
540 | } else if &x.function_name == "!" {
541 | if x.params.len() != 1 {
542 | panic!(
543 | "operator {} expected 1 parameters",
544 | (&x.function_name).as_str()
545 | );
546 | }
547 |
548 | self.process_expression(i, &x.params[0]);
549 | self.function_implementations[i].with_instructions(vec![
550 | F64_CONST,
551 | 0.0.into(),
552 | F64_EQ,
553 | F64_CONVERT_S_I32,
554 | ]);
555 | } else if &x.function_name == "~" {
556 | if x.params.len() != 1 {
557 | panic!(
558 | "operator {} expected 1 parameters",
559 | (&x.function_name).as_str()
560 | );
561 | }
562 |
563 | self.process_expression(i, &x.params[0]);
564 | self.function_implementations[i].with_instructions(vec![
565 | I64_TRUNC_S_F64,
566 | I64_CONST,
567 | (-1 as i32).into(),
568 | I64_XOR,
569 | F64_CONVERT_S_I64,
570 | ]);
571 | } else if &x.function_name == "and" {
572 | if x.params.len() != 2 {
573 | panic!(
574 | "operator {} expected 2 parameters",
575 | (&x.function_name).as_str()
576 | );
577 | }
578 |
579 | self.process_expression(i, &x.params[0]);
580 | self.function_implementations[i].with_instructions(vec![
581 | I64_TRUNC_S_F64,
582 | I64_CONST,
583 | 0.into(),
584 | I64_NE,
585 | ]);
586 | self.process_expression(i, &x.params[1]);
587 | self.function_implementations[i].with_instructions(vec![
588 | I64_TRUNC_S_F64,
589 | I64_CONST,
590 | 0.into(),
591 | I64_NE,
592 | I32_AND,
593 | F64_CONVERT_S_I32,
594 | ]);
595 | } else if &x.function_name == "or" {
596 | if x.params.len() != 2 {
597 | panic!(
598 | "operator {} expected 2 parameters",
599 | (&x.function_name).as_str()
600 | );
601 | }
602 |
603 | self.process_expression(i, &x.params[0]);
604 | self.function_implementations[i].with_instructions(vec![I64_TRUNC_S_F64]);
605 | self.process_expression(i, &x.params[1]);
606 | self.function_implementations[i].with_instructions(vec![
607 | I64_TRUNC_S_F64,
608 | I64_OR,
609 | I64_CONST,
610 | 0.into(),
611 | I64_NE,
612 | F64_CONVERT_S_I32,
613 | ]);
614 | } else {
615 | let (function_handle, _) = self
616 | .resolve_identifier(&x.function_name)
617 | .expect(&format!("{} is not a valid function", &x.function_name));
618 | for k in 0..x.params.len() {
619 | self.process_expression(i, &x.params[k])
620 | }
621 | self.function_implementations[i]
622 | .with_instructions(vec![CALL, (function_handle as i32).into()]);
623 | }
624 | }
625 | Expression::TextLiteral(x) => {
626 | let pos = self.get_or_create_text_data(&x);
627 | self.function_implementations[i]
628 | .with_instructions(vec![F64_CONST, (pos as f64).into()]);
629 | }
630 | Expression::Identifier(x) => {
631 | let val = self
632 | .resolve_identifier(&x)
633 | .expect(&format!("{} is not a valid identifier", &x));
634 | match val.1 {
635 | IdentifierType::Global => {
636 | self.function_implementations[i]
637 | .with_instructions(vec![F64_CONST, val.0.into()]);
638 | }
639 | IdentifierType::Local => {
640 | self.function_implementations[i]
641 | .with_instructions(vec![LOCAL_GET, (val.0 as i32).into()]);
642 | }
643 | IdentifierType::Function => {
644 | self.function_implementations[i]
645 | .with_instructions(vec![F64_CONST, val.0.into()]);
646 | }
647 | }
648 | }
649 | Expression::Number(x) => {
650 | self.function_implementations[i].with_instructions(vec![F64_CONST, (*x).into()]);
651 | }
652 | }
653 | }
654 |
655 | fn process_functions(&mut self) {
656 | // now lets process the insides of our functions
657 | for i in 0..self.function_defs.len() {
658 | if let TopLevelOperation::DefineFunction(f) = self.function_defs[i].clone() {
659 | self.local_names = f.params.clone();
660 | for j in 0..f.children.len() {
661 | self.process_expression(i, &f.children[j].clone());
662 | if j != f.children.len() - 1 {
663 | self.function_implementations[i].with_instructions(vec![DROP]);
664 | }
665 | }
666 | //end the function
667 | self.function_implementations[i].with_instructions(vec![END]);
668 | }
669 | }
670 |
671 | //now that we are done with everything, put funcions in the app
672 | let num_funcs = self.function_defs.len();
673 | for _ in 0..num_funcs {
674 | let f = self.function_implementations.remove(0);
675 | self.wasm.add_function(f);
676 | }
677 |
678 | self.wasm.add_elements(
679 | 0,
680 | self.function_names
681 | .iter()
682 | .enumerate()
683 | .map(|(i, _)| Element::new(i as u32))
684 | .collect::>(),
685 | )
686 | }
687 |
688 | fn complete(&mut self) -> Vec {
689 | self.wasm.to_bytes()
690 | }
691 | }
692 |
693 | pub fn compile(app: crate::ast::App) -> Result, Error> {
694 | let mut compiler = Compiler::new(app);
695 | compiler.pre_process_functions();
696 | compiler.process_globals();
697 | compiler.process_functions();
698 | compiler.set_heap_start();
699 | Ok(compiler.complete())
700 | }
701 |
--------------------------------------------------------------------------------