diff --git a/Cargo.lock b/Cargo.lock index ce454a9..2a3d033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,12 +18,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "ahash" version = "0.8.12" @@ -252,12 +246,6 @@ dependencies = [ "syn", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.11.1" @@ -325,12 +313,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.5" @@ -416,15 +398,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -528,31 +501,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "foldhash" version = "0.1.5" @@ -621,14 +575,10 @@ name = "game" version = "0.1.0" dependencies = [ "anyhow", - "bytemuck", "console_error_panic_hook", "console_log", "env_logger", - "glam", - "image", "log", - "mars", "pollster", "wasm-bindgen", "wasm-bindgen-futures", @@ -670,15 +620,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glam" -version = "0.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6" -dependencies = [ - "bytemuck", -] - [[package]] name = "glow" version = "0.17.0" @@ -784,20 +725,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-traits", - "png", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -906,12 +833,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" - [[package]] name = "js-sys" version = "0.3.98" @@ -1004,13 +925,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" - -[[package]] -name = "mars" -version = "0.1.0" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -1027,16 +944,6 @@ dependencies = [ "libc", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "naga" version = "29.0.3" @@ -1491,19 +1398,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polling" version = "3.11.0" @@ -1802,12 +1696,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "simd-adler32" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" - [[package]] name = "simd_cesu8" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index b6eda9b..81491de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,7 @@ env_logger = "0.11.10" log = "0.4" wgpu = "29.0.3" pollster = "0.4.0" -glam = { version = "0.33.3", features = [ "bytemuck" ] } console_error_panic_hook = "0.1.7" -bytemuck = { version = "1.25.0", features = [ "derive" ]} -image = { version = "0.24", default-features = false, features = ["png", "jpeg"]} -mars = { path = "../Mars" } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.6" diff --git a/src/assets/script.lua b/src/assets/script.lua new file mode 100644 index 0000000..73a6987 --- /dev/null +++ b/src/assets/script.lua @@ -0,0 +1,12 @@ +local two +do + local one + if something then + local three + print(something) + end + local four + if something2 then + + end +end \ No newline at end of file diff --git a/src/assets/shader.wgsl b/src/assets/shader.wgsl index 0aa3136..f5a0eae 100644 --- a/src/assets/shader.wgsl +++ b/src/assets/shader.wgsl @@ -1,35 +1,19 @@ -// Vertex shader -@group(1) @binding(0) -var view: mat4x4; - -struct VertexInput { - @location(0) position: vec3, - @location(1) tex_coords: vec2, -} - struct VertexOutput { @builtin(position) clip_position: vec4, - @location(0) tex_coords: vec2, -} +}; @vertex fn vs_main( - model: VertexInput, + @builtin(vertex_index) in_vertex_index: u32, ) -> VertexOutput { var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.clip_position = view * vec4(model.position, 1.0); + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; + out.clip_position = vec4(x, y, 0.0, 1.0); return out; } -// Fragment shader - -@group(0) @binding(0) -var t_diffuse: texture_2d; -@group(0) @binding(1) -var s_diffuse: sampler; - @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return textureSample(t_diffuse, s_diffuse, in.tex_coords); + return vec4(0.3, 0.2, 0.1, 1.0); } \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index a4a625a..4806748 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,73 +1,5 @@ -use std::collections::HashMap; // Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu use std::sync::Arc; -use glam::camera::lh::proj::directx::perspective; -use wgpu::util::DeviceExt; -use glam::prelude::*; - -struct Controller { - buttons: HashMap, - keys: HashMap, - mouse: Vec2, -} - -impl Controller { - fn new() -> Controller { - Controller { - buttons: Default::default(), - keys: HashMap::new(), - mouse: Vec2::new(0.0,0.0), - } - } -} - -struct Camera { - frame: Affine3A, - aspect_ratio: f32, - z_near: f32, - z_far: f32, - fov_y: f32, -} - -impl Camera { - fn view(&self) -> Mat4 { - let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); - projection * self.frame.inverse() - } - fn new(window: &Arc) -> Camera { - Camera { - aspect_ratio: window.inner_size().width as f32 / window.inner_size().height as f32, - frame: Affine3A::IDENTITY, - fov_y: 90.0, - z_near: 0.1, - z_far: 1000.0 - } - } - fn resize(&mut self, window: &Arc) { - self.aspect_ratio = window.inner_size().width as f32 / window.inner_size().height as f32 - } - fn control(&mut self, delta: Vec3) { - self.frame = self.frame * Affine3A::from_translation(delta); - } - fn rotate(&mut self, yaw: f32, pitch: f32) { - let (mut y,mut x,z) = self.frame.matrix3.to_euler(EulerRot::YXZ); - x = (x + pitch * 0.005).clamp(-1.4,1.4); - y = y + yaw * 0.005; - self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ,y,x,z); - } -} - - -const VERTICES: &[Vertex] = &[ - // Changed - Vertex { position: [-0.5,-0.5,-0.5], tex_coords: [0,0]} -]; - -const INDICES: &[u16] = &[ - 0, 1, 4, - 1, 2, 4, - 2, 3, 4, -]; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -82,94 +14,17 @@ use winit::{ window::Window, }; -type Kt = HashMap>; - -struct Renderer { - surface: (wgpu::Surface<'static>,bool), - config: wgpu::SurfaceConfiguration, +// This will store the state of our game +pub struct State { + surface: wgpu::Surface<'static>, device: wgpu::Device, queue: wgpu::Queue, - camera: Camera, - clear_color: wgpu::Color, -} - -struct UniformPlan { - bind_group: wgpu::BindGroup, - buffer: wgpu::Buffer, -} - -struct TexturePlan { - bind_group: wgpu::BindGroup, - texture: wgpu::Texture, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -struct SimpleVertex { - pos: [f32; 3], - uv: [f32; 2], - norm: [f32; 3], -} - -impl SimpleVertex { - fn desc() -> wgpu::VertexBufferLayout<'static> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, - shader_location: 1, - format: wgpu::VertexFormat::Float32x2, - }, - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, - shader_location: 2, - format: wgpu::VertexFormat::Float32x3, - } - ] - } - } -} - -struct SimpleMesh { - vertex_buffer: wgpu::Buffer, - index_buffer: wgpu::Buffer, - indices_count: u32, -} - -struct SimpleMaterial { - albedo: TexturePlan, - normal: Option, - specular: Option, -} - -struct SimpleModel { - affine: Affine3A, - material: mars::gc::Gc, - -} - -struct SimpleRenderPlan { - pipeline: wgpu::RenderPipeline, - // todo: clients: Kt>> - clients: Vec -} - -struct FluidRigidBody { - -} - -pub struct State { - renderer: Renderer, - controller: Controller, + config: wgpu::SurfaceConfiguration, + is_surface_configured: bool, window: Arc, - diffuse_bind_group: wgpu::BindGroup, + render_pipeline1: wgpu::RenderPipeline, + render_pipeline2: wgpu::RenderPipeline, + other_pipeline: bool, sky: wgpu::Color, } @@ -192,7 +47,7 @@ impl State { display: None, }); - let surface = instance.create_surface(window.clone())?; + let surface = instance.create_surface(window.clone()).unwrap(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { @@ -240,205 +95,22 @@ impl State { desired_maximum_frame_latency: 2, }; - let diffuse_bytes = include_bytes!("assets/test.png"); - let diffuse_image = image::load_from_memory(diffuse_bytes)?; - let diffuse_rgba = diffuse_image.to_rgba8(); - - use image::GenericImageView; - let dimensions = diffuse_image.dimensions(); - - println!("{:?}",dimensions); - - let texture_size = wgpu::Extent3d { - width: dimensions.0, - height: dimensions.1, - // All textures are stored as 3D, we represent our 2D texture - // by setting depth to 1. - depth_or_array_layers: 1, - }; - let diffuse_texture = device.create_texture( - &wgpu::TextureDescriptor { - size: texture_size, - mip_level_count: 1, // We'll talk about this a little later - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - // Most images are stored using sRGB, so we need to reflect that here. - format: wgpu::TextureFormat::Rgba8UnormSrgb, - // TEXTURE_BINDING tells wgpu that we want to use this texture in shaders - // COPY_DST means that we want to copy data to this texture - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - label: Some("diffuse_texture"), - // This is the same as with the SurfaceConfig. It - // specifies what texture formats can be used to - // create TextureViews for this texture. The base - // texture format (Rgba8UnormSrgb in this case) is - // always supported. Note that using a different - // texture format is not supported on the WebGL2 - // backend. - view_formats: &[], - } - ); - - queue.write_texture( - // Tells wgpu where to copy the pixel data - wgpu::TexelCopyTextureInfo { - texture: &diffuse_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - // The actual pixel data - &diffuse_rgba, - // The layout of the texture - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(4 * dimensions.0), - rows_per_image: Some(dimensions.1), - }, - texture_size, - ); - - let diffuse_texture_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::Repeat, - address_mode_v: wgpu::AddressMode::Repeat, - address_mode_w: wgpu::AddressMode::Repeat, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::MipmapFilterMode::Nearest, - ..Default::default() - }); - - let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - // This should match the filterable field of the - // corresponding Texture entry above. - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); - - let diffuse_bind_group = device.create_bind_group( - &wgpu::BindGroupDescriptor { - layout: &texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&diffuse_sampler), - } - ], - label: Some("diffuse_bind_group"), - } - ); - - let camera = Camera::new(&window); - - let camera_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[camera.view()]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - } - ); - - let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - } - ], - label: Some("camera_bind_group_layout"), - }); - - let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - } - ], - label: Some("camera_bind_group"), - }); - - let vertex_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(VERTICES), - usage: wgpu::BufferUsages::VERTEX, - } - ); - - let index_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(INDICES), - usage: wgpu::BufferUsages::INDEX, - } - ); - - let num_indices = INDICES.len() as u32; - let shader1 = device.create_shader_module(wgpu::include_wgsl!("assets/shader.wgsl")); let render_pipeline_layout1 = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - Some(&texture_bind_group_layout), - Some(&camera_bind_group_layout), - ], + bind_group_layouts: &[], immediate_size: 0, }); - /*let water_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Water Pipeline"), - layout: Some(&water_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader1, - entry_point: Some("vs_main"), - buffers: &[ - - ], - compilation_options: wgpu::PipelineCompilationOptions::default(); - } - });*/ - let render_pipeline1 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), layout: Some(&render_pipeline_layout1), vertex: wgpu::VertexState { module: &shader1, - entry_point: Some("vs_main"), - buffers: &[ - Vertex::desc(), - ], + entry_point: Some("vs_main"), // 1. + buffers: &[], // 2. compilation_options: wgpu::PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { @@ -475,28 +147,74 @@ impl State { cache: None, // 6. }); + let shader2 = device.create_shader_module(wgpu::include_wgsl!("assets/shader2.wgsl")); + + let render_pipeline_layout2 = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + immediate_size: 0, + }); + + let render_pipeline2 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout2), + vertex: wgpu::VertexState { + module: &shader2, + entry_point: Some("vs_main"), // 1. + buffers: &[], // 2. + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + // 3. + module: &shader2, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + // 4. + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, // 1. + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, // 2. + cull_mode: Some(wgpu::Face::Back), + // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE + polygon_mode: wgpu::PolygonMode::Fill, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: None, // 1. + multisample: wgpu::MultisampleState { + count: 1, // 2. + mask: !0, // 3. + alpha_to_coverage_enabled: false, // 4. + }, + multiview_mask: None, // 5. + cache: None, // 6. + }); + Ok(Self { surface, - camera_buffer, - camera_bind_group, - diffuse_bind_group, - index_buffer, - vertex_buffer, - controller: Controller::new(), device, queue, config, is_surface_configured: false, window, - num_indices, - render_pipeline: render_pipeline1, + render_pipeline1, + render_pipeline2, + other_pipeline: false, sky: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, - camera, }) } @@ -506,7 +224,6 @@ impl State { self.config.width = width.min(max); self.config.height = height.min(max); self.surface.configure(&self.device, &self.config); - self.camera.resize(&self.window); self.is_surface_configured = true; } } @@ -571,47 +288,12 @@ impl State { multiview_mask: None, }); - let mut movement = Vec3::new(0.0,0.0,0.0); - - let pressed = |keycode: KeyCode| { - if let Some(true) = self.controller.keys.get(&keycode) { - true - } else { - false - } - }; - - if pressed(KeyCode::KeyA) { - movement.x -= 1.0; + if self.other_pipeline { + render_pass.set_pipeline(&self.render_pipeline1); + } else { + render_pass.set_pipeline(&self.render_pipeline2); } - if pressed(KeyCode::KeyD) { - movement.x += 1.0; - } - if pressed(KeyCode::KeyW) { - movement.z += 1.0; - } - if pressed(KeyCode::KeyS) { - movement.z -= 1.0; - } - if pressed(KeyCode::KeyE) { - movement.y += 1.0; - } - if pressed(KeyCode::KeyQ) { - movement.y -= 1.0; - } - - println!("{:?} {:?} {:?}",movement,self.camera.frame.translation,self.camera.frame.matrix3.to_euler(EulerRot::YXZ)); - - self.camera.control(movement * 0.1); - - self.queue.write_buffer(&self.camera_buffer,0,bytemuck::cast_slice(&[self.camera.view()])); - - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(1, &self.camera_bind_group, &[]); - render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.draw_indexed(0..self.num_indices, 0, 0..1); + render_pass.draw(0..3, 0..1); // 3. } // submit will accept anything that implements IntoIter @@ -624,13 +306,11 @@ impl State { fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) { match (code, is_pressed) { (KeyCode::Escape, true) => event_loop.exit(), - (KeyCode::Space, true) => {}, - (KeyCode::KeyR, true) => { - self.camera.frame = Affine3A::IDENTITY + (KeyCode::Space, true) => { + self.other_pipeline = !self.other_pipeline; } _ => {} } - self.controller.keys.insert(code, is_pressed); } fn handle_mouse_moved(&mut self, position: PhysicalPosition) { @@ -639,15 +319,7 @@ impl State { g: position.x / 1000.0, b: position.y / 1000.0, a: 1.0, - }; - if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { - self.camera.rotate(position.x as f32 - self.controller.mouse.x, position.y as f32 - self.controller.mouse.y); } - self.controller.mouse = Vec2::new(position.x as f32, position.y as f32); - } - - fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState ) { - self.controller.buttons.insert(button,state.is_pressed()); } } @@ -756,7 +428,6 @@ impl ApplicationHandler for App { } } } - WindowEvent::MouseInput { button, state: element, .. } => state.handle_mouse_button(button,element), WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos), WindowEvent::KeyboardInput { event: diff --git a/src/lang.rs b/src/lang.rs new file mode 100644 index 0000000..e882a55 --- /dev/null +++ b/src/lang.rs @@ -0,0 +1,1243 @@ +use std::any::Any; +use std::collections::HashMap; +use std::fmt::Formatter; +use std::ops::Add; +use std::rc::Rc; +// TODO: Vec> is good but sometimes we either need at least one or none, current fix is just to check if there is one and if not skip it, this takes extra performance + +#[derive(Clone, Copy)] +struct Reader<'a> { + slice: &'a str, + full: &'a str, + white: bool, // Is the previous 'thing' whitespace? +} + +struct Error(String,Piece<()>); + +impl Error { + fn new(message: &str, reader: &Reader) -> Result { + Err(Error(message.to_string(), reader.marker())) + } + fn msg(&self, reader: &Reader) -> String { + self.0.clone().add(reader.slice) + } +} + +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Error: {} {}", self.0, self.1.index)) + } +} + + +type Comment = String; + +struct Piece { + comment: Comment, + index: usize, + width: usize, + it: T, +} + +impl std::fmt::Debug for Piece where T: std::fmt::Debug { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.it.fmt(f) + } +} + + +impl Piece { + fn of(it: T, reader: &Reader, width: usize) -> Piece { + Piece { + it, + index: reader.slice.as_ptr() as usize - reader.full.as_ptr() as usize, + width, + comment: String::new() + } + } + fn from(it: T, begin: &Piece, end: &Piece) -> Piece { + Piece { + it, + index: begin.index, + width: end.index + end.width - begin.index, + comment: begin.comment.clone() + } + } + fn unit(&self) -> Piece<()> { + Piece { + it: (), + index: self.index, + width: self.width, + comment: self.comment.clone() + } + } + fn map(self, f: F) -> Piece where F: FnOnce(T) -> U { + let it = f(self.it); + Piece { + it, + index: self.index, + width: self.width, + comment: self.comment // this is stupid + } + } + fn new(it: T) -> Piece { + Piece { + it, + index: 0, + width: 0, + comment: String::new() + } + } +} + +// Strips comments and whitespace +// todo: implement comments returning for debugging. + +impl<'a> From<&'a str> for Reader<'a> { + fn from(str: &'a str) -> Self { + Reader { + full: str, + slice: str, + white: true, + } + } +} + +impl<'a> Reader<'a> { + fn trim(&mut self) -> Result { + let start = self.slice.as_ptr() as usize; + self.slice = self.slice.trim_start(); + if self.slice.starts_with("--") { + let comment = if self.slice[2..].starts_with("[[") { + match self.slice.find("]]") { + Some(i) => { + let comment = &self.slice[4..i + 2]; + self.slice = &self.slice[i + 2..].trim(); + self.white = true; + Ok(comment) + } + _ => self.error("Unterminated multi-line comment"), + } + } else { + let comment; + if let Some(i) = self.slice.find("\n") { + self.slice = &self.slice[i + 1..].trim(); + comment = &self.slice[2..i+1]; + self.white = true; + } else { + self.slice = &self.slice[..self.slice.len() - 1]; + comment = &self.slice[2..]; + self.white = false; + } + Ok(comment) + }?; + self.trim().map(|it| { // Try another comment and append them to each other, just in case + it + "\n" + comment + }) + } else { + if start < self.slice.as_ptr() as usize { + self.white = true; + } + Ok(String::new()) + } + } + fn white(&mut self) -> Result<&mut Self, Error> { + if self.white { + Ok(self) + } else { + self.error("Expected whitespace") + } + } + fn consume(&mut self, start: &str) -> Result<&mut Self, Error> { + self.trim()?; + if self.slice.starts_with(start) { + self.slice = &self.slice[start.len()..]; + Ok(self) + } else { + self.error(format!("Expected '{}'", start).as_str()) + } + } + // Consume but require whitespace before + fn white_consume(&mut self, start: &str) -> Result<&mut Self, Error> { + if self.white { + self.consume(start) + } else { + self.error(format!("Expected whitespace before parsing {}",start).as_str()) + } + } + // Consume but set whitespace after + fn consume_white(&mut self, start: &str) -> Result<&mut Self, Error> { + let result = self.consume(start)?; + result.white = true; + Ok(result) + } + fn take(&mut self) -> Result, Error> + where + T: Parse, + { + T::consume(self) + } + fn finished(&self) -> bool { + self.slice.is_empty() + } + fn error(&self, message: &str) -> Result { + Error::new(message, self) + } + fn marker(&self) -> Piece<()> { + Piece::of((), self, 0) + } +} + +#[derive(Debug)] +struct Number(String); +#[derive(Debug, Clone)] +struct Name(String); +// Requirements for Parse: +// - The reader.slice must be pushed to the end of the consumed text (or restored in an error) +// - ??? +// - The reader may have not been trimmed +trait Parse { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized; +} + +static KEYWORDS: &[&str] = &["and","break","do","else","elseif","end","false","for","function","func","global","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"]; +impl Parse for Name { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized, + { + reader.trim()?; + reader.white()?; + let mut chars = reader.slice.chars(); + let mut end = 1; + match chars.next() { + None => { + return reader.error("Expected identifier"); + } + Some(c) => { + if !(c.is_alphabetic() || c == '_') { + return reader.error("Identifier must begin with alphabetic character"); + } + } + }; + end += chars + .take_while(|c| c.is_alphanumeric() || *c == '_') + .count(); + let name = reader.slice[..end].to_string(); + // TODO: do we need to check if this is a keyword here? + if KEYWORDS.contains(&name.as_str()) { + return reader.error(format!("Expected name, got keyword {}",name).as_str()) + } + let result = Ok(Piece::of( + Name(name), + reader, + end, + )); + reader.slice = &reader.slice[end..]; + result + } +} +impl Parse for Number { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized, + { + reader.trim()?; + reader.white()?; + if !reader.white { + return reader.error("Expected whitespace before parsing number"); + } + let mut end; + if reader.slice.starts_with("0x") { + end = reader.slice[2..] + .chars() + .take_while(|c| c.is_digit(16)) + .count() + + 2; + if end == 2 { + end = 0 + } + } else { + end = reader + .slice + .chars() + .take_while(|c| c.is_digit(10) || *c == '.') + .count(); + } + if end == 0 { + reader.error("Could not parse number".into()) + } else { + reader.slice = &reader.slice[end..]; + Ok(Piece::of( + Number(reader.slice[..end].to_string()), + reader, + end, + )) + } + } +} + +impl Parse for String { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized, + { + reader.trim()?; + let chars: &mut std::str::Chars = &mut reader.slice.chars(); + let mut string = String::new(); + let term1: char; + let term2: Option; + let extra; + match chars.next() { + Some('"') => { + term1 = '"'; + term2 = None; + extra = 2; + } + Some('\'') => { + term1 = '\''; + term2 = None; + extra = 2; + } + Some('[') if chars.next().is_some_and(|it| it == '[') => { + term1 = ']'; + term2 = Some(']'); + extra = 4; + } + _ => { + return reader.error("Could not parse string"); + } + } + loop { + match chars.next() { + Some(c) => { + match c { + '\\' => match chars.next() { + Some(c) => match c { + 'n' => string.push('\n'), + 'r' => string.push('\r'), + 't' => string.push('\t'), + '"' => string.push('"'), + '\'' => string.push('\''), + '\\' => string.push('\\'), + it => { + string.push(it); + } + }, + None => {} + }, + '\r' => {} // todo: ? + '\n' if term2.is_none() => { + return reader.error("Found newline while parsing string"); + } + term if term == term1 => { + // If the second terminator exists and is the next char, or there is no second terminator + if !term2.is_some() + || chars.next().is_some_and(|it| it == term2.unwrap()) + { + let length = string.len(); + reader.slice = &reader.slice[length + extra..]; + return Ok(Piece::of(string, reader, length)); + // There is a second terminator and it wasn't the next char + } else { + string.push(term) + } + } + _ => { + string.push(c); + } + } + } + None => { + return reader.error("Found EOF while parsing string"); + } + } + } + } +} + +// AST +#[derive(Debug)] +struct Block(Vec>, Vec>); // +#[derive(Debug)] +struct Chunk(Piece); +#[derive(Debug)] +enum Statement { + Semicolon, + Assignment(Vec>, Vec>), + Call(Call), + Label(Piece), + Break, + Continue, + Goto(Piece), + Do(Block), + While(Piece, Block), + Repeat(Block, Piece), + If(Vec>, Vec, Option), + Range( + Piece, + Piece, + Piece, + Option>, + Block, + ), + Iterator(Vec>, Vec>, Block), + Function(Vec>, Option>, Function), + LocalFunction(Piece, Function), + Declaration(Vec>, Vec>), +} +#[derive(Debug)] +enum Variable { + Name(Name), + Index(Box>, Piece), +} +#[derive(Debug)] +enum Expression { + Nil, + True, + False, + Number(Number), + String(String), + VarArg(VarArg), + Function(Function), + Prefix(Box), + Table(Constructor), + Binary(Box>, Piece, Box>), + Unary(Piece, Box>), +} +#[derive(Debug)] +struct Constructor(Vec>); +#[derive(Debug)] +enum Prefix { + Ident(Variable), + Call(Call), + Expression(Piece), +} +#[derive(Debug)] +struct VarArg(Option); +#[derive(Debug)] +struct Call(Box>, Option>, Piece); +#[derive(Debug)] +struct Args(Vec>); +#[derive(Debug)] +struct Function(Vec>, Option>, Piece); +#[derive(Debug)] +struct Field(Option>, Piece); +#[derive(Debug)] +enum FieldSep { + Comma, + Semicolon, +} +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] +enum BinaryOp { + Or, + And, + Less, + Greater, + LessEqual, + GreatEqual, + Equivalent, + NotEqual, + Pipe, + Tilde, + Ampersand, + ShiftLeft, + ShiftRight, + Concat, + Plus, + Minus, + Mul, + Divide, + DivFloor, + Modulo, + Caret, +} +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] +enum UnaryOp { + Negate, + Not, + Ampersand, + Tilde +} + +impl Parse for Vec> { + fn consume(reader: &mut Reader) -> Result>>, Error> + where + Self: Sized, + { + let mut result: Vec> = Vec::new(); + reader.trim()?; + result.push(reader.take::()?); + while reader.consume_white(",").is_ok() { + result.push(reader.take::()?); + } + let first = result.first().unwrap().unit(); + let last = result.last().unwrap().unit(); + Ok(Piece::from(result, &first, &last)) + } +} + +impl Parse for BinaryOp { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let mut chars = reader.slice.chars(); + if let Some(char) = chars.next() { + let mut count = 1; + let op = match char { + '+' => BinaryOp::Plus, + '-' => BinaryOp::Minus, + '*' => BinaryOp::Mul, + '/' => { + if let Some('/') = chars.next() { + count += 1; + BinaryOp::DivFloor + } else { + BinaryOp::Divide + } + }, + '^' => BinaryOp::Caret, + '%' => BinaryOp::Modulo, + '&' => BinaryOp::Ampersand, + '~' => { + match chars.next() { + Some('=') => { + count += 1; + BinaryOp::NotEqual + }, + _ => { + BinaryOp::Tilde + } + } + }, + '|' => BinaryOp::Pipe, + '>' => { + match chars.next() { + Some('>') => { + count += 1; + BinaryOp::ShiftRight + }, + Some('=') => { + count += 1; + BinaryOp::GreatEqual + } + _ => { + BinaryOp::Greater + } + } + }, + '<' => { + match chars.next() { + Some('<') => { + count += 1; + BinaryOp::ShiftLeft + }, + Some('=') => { + count += 1; + BinaryOp::LessEqual + } + _ => { + BinaryOp::Less + } + } + }, + '.' => { + if let Some('.') = chars.next() { + count += 1; + BinaryOp::Concat + } else { + return reader.error("Expected '.' for concatenation") + } + }, + '=' => { + if let Some('=') = chars.next() { + count += 1; + BinaryOp::Equivalent + } else { + return reader.error("Expected '=' for equality") + } + }, + _ => { + if reader.white_consume("and").is_ok() { + count+=2; + BinaryOp::And + } else if reader.white_consume("or").is_ok() { + count+=1; + BinaryOp::Or + } else { + return reader.error("Expected binary operation") + } + } + }; + if !matches!(op,BinaryOp::And | BinaryOp::Or) { + reader.white = true; + } + let result = Ok(Piece::of(op,reader,count)); + reader.slice = &reader.slice[count..]; + result + } else { + reader.error("Expected binary operand, got EOF") + } + } +} + +impl Parse for UnaryOp { + fn consume(reader: &mut Reader) -> Result, Error> { + let start = &reader.marker(); + reader.trim()?; + let op = if reader.consume("-").is_ok() { + UnaryOp::Negate + } else if reader.white_consume("not").is_ok() { + UnaryOp::Not + } else if reader.consume("#").is_ok() { + UnaryOp::Ampersand + } else if reader.consume("~").is_ok() { + UnaryOp::Tilde + } else { + return reader.error("Expected unary operand") + }; + if !matches!(op,UnaryOp::Not) { + reader.white = true; + } + Ok(Piece::from(op, &start, &reader.marker())) + } +} + +impl Parse for Function { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + reader.consume_white("(")?; + let mut parameters: Vec> = Vec::new(); + let mut vararg = None; + while let Ok(name) = reader.take::() { + parameters.push(name); + if vararg.is_some() { + return reader.error("Unexpected argument after vararg '...'"); + } + if !reader.consume_white(",").is_ok() { + break; + } + vararg = reader.take::().ok() + } + reader.consume_white(")")?; + let end = reader.marker(); + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Function(parameters, vararg, block), + &start, + &end, + )) + } +} + +impl Parse for VarArg { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + reader.white()?.consume_white("...")?; + let name = reader.take::().ok(); + let end; + if let Some(name) = name { + let end = name.unit(); + Ok(Piece::from(VarArg(Some(name.it)), &start, &end)) + } else { + end = reader.marker(); + Ok(Piece::from( + VarArg(name.map(|piece| piece.it)), + &start, + &end, + )) + } + } +} + +impl BinaryOp { + fn compare(&self, other: &BinaryOp) -> bool { + self < other + } + fn compare_unary(&self) -> bool { + match self { + BinaryOp::Caret => true, + _ => false, + } + } +} + +fn inject(current: Piece, op: Piece, next: Piece) -> Piece { + let start = current.unit(); + if let Expression::Binary(left,op2,right) = current.it { + if op.it.compare(&op2.it) { + let start = left.unit(); + let new_right = Box::new(inject(*right, op, next)); + let end = new_right.unit(); + Piece::from(Expression::Binary(left,op2,new_right),&start,&end) + } else { + let start2 = start.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Binary(left,op2,right))),op,Box::new(next)),&start2,&end) + } + } else if let Expression::Unary(op2,inner) = current.it { + if op.it.compare_unary() { + let start = op2.unit(); + let inner = Box::new(inject(*inner, op, next)); + let end = inner.unit(); + Piece::from(Expression::Unary(op2,inner),&start,&end) + } else { + let start2 = start.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Unary(op2,inner))),op,Box::new(next)),&start2,&end) + } + } else { + let start = current.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(current),op,Box::new(next)),&start,&end) + } +} + +impl Parse for Expression { + fn consume(reader: &mut Reader) -> Result, Error> { + let taker = |reader: &mut Reader| { + let start = reader.marker(); + reader.trim()?; + let result = if reader.white_consume("nil").is_ok() { + Expression::Nil + } else if reader.white_consume("true").is_ok() { + Expression::True + } else if reader.white_consume("false").is_ok() { + Expression::False + } else if let Ok(vararg) = reader.take::() { + Expression::VarArg(vararg.it) + } else if let Ok(number) = reader.take::() { + Expression::Number(number.it) + } else if let Ok(string) = reader.take::() { + Expression::String(string.it) + } else if reader.white_consume("function").is_ok() || reader.white_consume("func").is_ok() { + Expression::Function(reader.take::()?.it) + } else if let Ok(table) = reader.take::() { + Expression::Table(table.it) + } else if let Ok(unary) = reader.take::() { + Expression::Unary(unary,Box::new(reader.take::()?)) + } else { + Expression::Prefix(Box::new(reader.take::()?.it)) + }; + Ok(Piece::from(result,&start,&reader.marker())) + }; + let mut current = taker(reader)?; + while let Ok(binary) = reader.take::() { + let next = taker(reader)?; + current = inject(current,binary,next); + } + Ok(current) + } +} + +impl Parse for Call { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let prefix = reader.take::()?; + let piece = prefix.unit(); + if let Prefix::Call(call) = prefix.it { + Ok(piece.map(|_| call)) + } else { + reader.error("Expected variable, found expression.") + } + } +} + +impl Parse for Field { + fn consume(reader: &mut Reader) -> Result, Error> { + let start = reader.marker(); + reader.trim()?; + let result = if reader.consume_white("[").is_ok() { + let index = reader.take::()?; + reader.consume_white("=")?; + let value = reader.take::()?; + reader.consume_white("]")?; + Field(Some(index),value) + } else if let Ok(name) = reader.take::() { + reader.consume_white("=")?; + let index = name.map(|it| Expression::String(it.0)); + let value = reader.take::()?; + Field(Some(index),value) + } else { + Field(None,reader.take::()?) + }; + Ok(Piece::from(result, &start, &reader.marker())) + } +} + +impl Parse for Constructor { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + reader.consume_white("{")?; + let fields: Piece>>; + if reader.clone().take::().is_ok() { + fields = reader.take::>>()?; + } else { + fields = Piece::from(Vec::new(),&reader.marker(),&reader.marker()); + } + reader.consume_white("}")?; + Ok(fields.map(|it| Constructor(it))) + } +} + +impl Parse for Args { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + let result = if reader.consume_white("(").is_ok() { + let expressions = reader.take::>>()?; + reader.consume_white(")")?; + Args(expressions.it) + } else if let Ok(table) = reader.take::() { + Args(vec!(table.map(|it| Expression::Table(it)))) + } else if let Ok(string) = reader.take::() { + Args(vec!(string.map(|it| Expression::String(it)))) + } else { + return reader.error("Expected arguments") + }; + Ok(Piece::from(result, &start, &reader.marker())) + } +} + +impl Parse for Prefix { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + let mut current: Box> = if reader.consume_white("(").is_ok() { + let exp = reader.take::()?; + reader.consume_white(")")?; + Box::new(Piece::from(Prefix::Expression(exp),&start,&reader.marker())) + } else if let Ok(name) = reader.take::() { + Box::new(Piece::from(Prefix::Ident(Variable::Name(name.it)),&start,&reader.marker())) + } else { + return reader.error("Expected expression") + }; + loop { + let start = reader.marker(); + let result: Prefix = if reader.consume_white("[").is_ok() { + let exp = reader.take::()?; + reader.consume_white("]")?; + Prefix::Ident(Variable::Index(current, exp)) + } else if reader.consume_white(".").is_ok() { + let field = reader.take::()?; + Prefix::Ident(Variable::Index(current,field.map(|it| Expression::String(it.0)))) + } else if reader.consume_white(":").is_ok() { + let field = reader.take::()?; + let args = reader.take::()?; + Prefix::Call(Call(current,Some(field),args)) + } else if let Ok(args) = reader.take::() { + Prefix::Call(Call(current,None,args)) + } else { + break + }; + current = Box::new(Piece::from(*Box::new(result), &start, &reader.marker())); + } + Ok(*current) + } +} + +impl Parse for Variable { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let prefix = reader.take::()?; + let piece = prefix.unit(); + if let Prefix::Ident(var) = prefix.it { + Ok(piece.map(|_| var)) + } else { + reader.error("Expected variable, found expression.") + } + } +} + +impl Parse for Statement { + fn consume(reader: &mut Reader) -> Result, Error> { + reader.trim()?; + let start = reader.marker(); + // todo: it is safer to pass back the new reader in the Ok, and then map it out + if reader.white_consume("break").is_ok() { + Ok(Piece::from(Statement::Break, &start, &reader.marker())) + } else if reader.white_consume("continue").is_ok() { + Ok(Piece::from(Statement::Continue, &start, &reader.marker())) + } else if reader.consume_white(";").is_ok() { + Ok(Piece::from(Statement::Semicolon, &start, &reader.marker())) + } else if reader.white_consume("local").is_ok() { + if reader.white_consume("function").is_ok() || + reader.white_consume("func").is_ok() { + let name = reader.take::()?; + let function = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::LocalFunction(name, function.it), + &start, + &reader.marker(), + )) + } else { + let names = reader.take::>>()?.it; + reader.consume_white("=")?; + let expressions = reader.take::>>()?.it; + *reader = *reader; + Ok(Piece::from( + Statement::Declaration(names, expressions), + &start, + &reader.marker(), + )) + } + } else if reader.white_consume("function").is_ok() || + reader.white_consume("func").is_ok() { + let mut names = Vec::new(); + loop { + if let Ok(name) = reader.take::() { + names.push(name); + if !reader.consume_white(".").is_ok() { + break; + } + } + } + let mut member = None; + if reader.consume_white(":").is_ok() { + member = Some(reader.take::()?); + } + let function = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Function(names, member, function.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("for").is_ok() { + let names = reader.take::>>()?.it; + let result = if names.len() < 1 { + let name = names.first().unwrap(); + reader.consume_white("=")?; + let begin = reader.take::()?; + reader.consume_white(",")?; + let finish = reader.take::()?; + let delta = if reader.consume(",").is_ok() { + Some(reader.take::()?) + } else { + None + }; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Statement::Range(Piece { + it: name.it.clone(), + width: name.width, + index: name.index, + comment: name.comment.clone(), + }, begin, finish, delta, block.it), + &start, + &reader.marker(), + )) + } else { + reader.white_consume("in")?; + let expressions = reader.take::>>()?.it; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Statement::Iterator(names, expressions, block.it), + &start, + &reader.marker(), + )) + }; + *reader = *reader; + result + } else if reader.white_consume("while").is_ok() { + let condition = reader.take::()?; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + *reader = *reader; + Ok(Piece::from( + Statement::While(condition, block.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("if").is_ok() { + let mut conditions = Vec::new(); + let mut blocks = Vec::new(); + let mut branch = |reader: &mut Reader| -> Result<(), Error> { + conditions.push(reader.take::()?); + reader.white_consume("then")?; + blocks.push(reader.take::()?.it); + Ok(()) + }; + branch(reader)?; + let mut block = None; + while reader.white_consume("else").is_ok() { + if reader.consume("if").is_ok() { + branch(reader)?; + } else { + block = Some(reader.take::()?.it); + break + } + } + reader.white_consume("end")?; + *reader = *reader; + Ok(Piece::from( + Statement::If(conditions, blocks, block), + &start, + &reader.marker(), + )) + } else if reader.white_consume("goto").is_ok() { + let name = reader.take::()?; + *reader = *reader; + Ok(Piece::from(Statement::Goto(name), &start, &reader.marker())) + } else if reader.white_consume("do").is_ok() { + let block = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Do(block.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("repeat").is_ok() { + reader.white_consume("repeat")?; + let block = reader.take::()?; + reader.white_consume("until")?; + let condition = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Repeat(block.it, condition), + &start, + &reader.marker(), + )) + } else if reader.consume_white("::").is_ok() { + let name = reader.take::()?; + reader.consume_white("::")?; + *reader = *reader; + Ok(Piece::from( + Statement::Label(name), + &start, + &reader.marker(), + )) + } else { + let mut clone = reader.clone(); + if let Ok(call) = clone.take::() { + *reader = clone; + Ok(call.map(|it| Statement::Call(it))) + } else { + let mut clone = reader.clone(); + let vars = clone.take::>>()?; + clone.consume("=")?; + let expressions = clone.take::>>()?; + *reader = clone; + Ok(Piece::from(Statement::Assignment(vars.it,expressions.it),&start,&reader.marker())) + } + } + } +} + +impl Parse for Block { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized, + { + let mut statements = Vec::new(); + let start = reader.marker(); + while let Ok(statement) = reader.take::() { + statements.push(statement); + } + let expressions = if reader.consume("return").is_ok() { + if reader.clone().take::().is_ok() { // TODO: make this dirty check less silly + let result = reader.take::>>()?; + reader.consume_white(";").ok(); + result.it + } else { + Vec::new() + } + } else { + Vec::new() + }; + Ok(Piece::from( + Block(statements, expressions), + &start, + &reader.marker(), + )) + } +} + +impl Parse for Chunk { + fn consume(reader: &mut Reader) -> Result, Error> + where + Self: Sized, + { + let mut statements = Vec::new(); + let start = reader.marker(); + while !reader.finished() { + let statement = reader.take::()?; + statements.push(statement); + reader.trim()?; + } + let expressions = if reader.white_consume("return").is_ok() { + let result = reader.take::>>()?; + reader.consume_white(";").ok(); + result.it + } else { + Vec::new() + }; + Ok(Piece::from( + Chunk(Piece::from( + Block(statements, expressions), + &start, + &reader.marker(), + )), + &start, + &reader.marker(), + )) + } +} + +fn parse(str: &str) -> Result { + let mut reader = Reader::from(str); + reader.take::().map(|it| it.it) +} + +// COMPILER + +#[derive(Debug)] +struct Table(HashMap, Option>); + +#[derive(Debug, Clone)] +enum Object { + Nil, + Bool(bool), + Number(f64), + Table(Rc), + String(String), + Object(Rc,Option>), +} + +struct Frame<'a> { + begin: Index, // Where this frame begins in the stack (after this) + base: Index, // Where arguments begin (before this) + parent: &'a mut Frame<'a>, +} + +#[derive(Clone)] +enum Register { + Upvalue(Index), + Stack(Index), + Label(Index), + VarArg, +} + +struct Context<'a> { + code: Vec, + symbols: HashMap, + parent: Option<&'a mut Context<'a>>, +} + +impl<'a> Context<'a> { + fn new() -> Context<'a> { + Context { + code: Vec::new(), + symbols: HashMap::new(), + parent: None, + } + } + fn from(&'a mut self) -> Context<'a> { + Context { + code: Vec::new(), + symbols: HashMap::new(), + parent: Some(self) + } + } + fn commit(&mut self) { + + } + fn compile(&mut self, it: &T) -> Result<(), Error> where T: Compile { + it.compile(self) + } + fn function(self) -> Callable { + Callable::Function(self.code) + } +} + +struct Machine { + stack: [Object; 1024], +} + +#[derive(Clone)] +struct Native(Rc Result<(),Error>>); + +impl std::fmt::Debug for Native { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +#[derive(Debug, Clone)] +enum Callable { + Rust(Native), + Function(Vec), + Closure(Vec,Vec>) +} + +type Index = i16; +type Count = i16; + +#[derive(Debug, Clone)] +enum Literal { + Nil, + Table, + Bool(bool), + Number(f64), + String(String), + Function(Callable), +} + +#[derive(Debug, Clone)] +enum Code { + Call(Callable,Count), + Literal(Literal), + Push(Index), + Binary(BinaryOp), + Unary(UnaryOp), + Up(Index), + Pop(Count), + Jump(Index), + Return(Count), +} + +trait Compile { + fn compile<'a>(&self, context: &mut Context<'a>) -> Result<(),Error>; +} + +impl Compile for Chunk { + fn compile<'a>(&self, context: &mut Context<'a>) -> Result<(), Error> { + Ok(()) + } +} + +impl Machine { + fn load(&self, source: &str) -> Result { + let chunk = parse(source)?; + let mut context = Context::new(); + context.compile(&chunk)?; + Ok(context.function()) + } + fn new() -> Machine { + Machine { + stack: [const {Object::Nil}; 1024] + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn simple() { + let mut machine = Machine::new(); + let result = machine.load(include_str!("assets/script.lua")); + println!("{:#?}", result); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index db89b9b..911bacc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ #![recursion_limit = "256"] -pub mod engine; \ No newline at end of file +pub mod engine; +pub mod lang; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 899b868..758c277 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,5 +3,6 @@ use game::*; fn main() { - engine::run().unwrap(); + + //engine::run().unwrap(); }