From 2f1cfdb329e543ee822f3f17fee8efcbd4035b4a Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Sat, 26 Mar 2022 00:50:35 -0600 Subject: [PATCH] Remove wasm --- Cargo.lock | 112 ------------ Cargo.toml | 12 +- out.log | 1 - package-lock.json | 29 --- package.json | 27 --- src/ast.rs | 195 -------------------- src/codegen.rs | 186 +------------------ src/lib.rs | 41 +++-- src/main.rs | 9 +- src/parse.rs | 446 +++++++++------------------------------------- src/tokenize.rs | 164 +++++++++++++++++ test.js | 21 --- test.pvt | 3 +- test.txt | 15 -- 14 files changed, 282 insertions(+), 979 deletions(-) delete mode 100644 out.log delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 src/ast.rs create mode 100644 src/tokenize.rs delete mode 100644 test.js delete mode 100644 test.txt diff --git a/Cargo.lock b/Cargo.lock index a5181d3..c78f37c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,118 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "leb128" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" - -[[package]] -name = "nyst" -version = "0.5.2" - [[package]] name = "pivot" version = "0.1.0" -dependencies = [ - "nyst", - "ron", - "serde", - "wat", -] - -[[package]] -name = "proc-macro2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ron" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a58080b7bb83b2ea28c3b7a9a994fd5e310330b7c8ca5258d99b98128ecfe4" -dependencies = [ - "base64", - "bitflags", - "serde", -] - -[[package]] -name = "serde" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1e438504729046a5cfae47f97c30d6d083c7d91d94603efdae3477fc070d4c" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "wast" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0586061bfacc035034672c8d760802b428ab4c80a92e2a392425c516df9be1" -dependencies = [ - "leb128", -] - -[[package]] -name = "wat" -version = "1.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d55b5ec4f9d9396fa99abaafa0688597395e57827dffd89731412ae90c9bf" -dependencies = [ - "wast", -] diff --git a/Cargo.toml b/Cargo.toml index 9e08fe1..69852c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,8 @@ [package] name = "pivot" version = "0.1.0" -authors = ["Garen Tyler "] +authors = ["Garen Tyler "] edition = "2018" -description = "Pivot is a new programming language built with Rust by Garen Tyler. Pivot is currently in the alpha stage of development." +description = "Pivot is a new programming language built with Rust. Pivot is currently in the alpha stage of development." license = "MIT" -repository = "https://github.com/garentyler/pivot" - -[dependencies] -ron = "0.6.2" -serde = "1.0.117" -wat = "1.0.29" -nyst = { path = "../nyst" } +repository = "https://github.com/garentyler/pivot" \ No newline at end of file diff --git a/out.log b/out.log deleted file mode 100644 index abb3c49..0000000 --- a/out.log +++ /dev/null @@ -1 +0,0 @@ -tokens: [String("a"), Plus, String("b")] diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b079809..0000000 --- a/package-lock.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "pivot", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "wasi": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/wasi/-/wasi-0.0.6.tgz", - "integrity": "sha512-D3f4Q/3LojW1uMloOC1OuFlGy2u22hLqpskAz0OkmoYeWIwVYf8XBuc9ihDGJnNf3lfNvqM4s0030sN0UUXExw==", - "requires": { - "bindings": "^1.5.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 31d73ac..0000000 --- a/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "pivot", - "version": "0.1.0", - "description": "Pivot is a new programming language built with Rust by Garen Tyler. Pivot is currently in the alpha stage of development.", - "main": "test.js", - "private": true, - "dependencies": { - "wasi": "0.0.6" - }, - "devDependencies": {}, - "scripts": { - "clean": "cargo clean && rm -rf node_modules out.wasm", - "build": "cargo build", - "run": "cargo run && node test.js", - "test": "cargo test" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/ElementG9/pivot.git" - }, - "author": "Garen Tyler", - "license": "MIT", - "bugs": { - "url": "https://github.com/ElementG9/pivot/issues" - }, - "homepage": "https://github.com/ElementG9/pivot#readme" -} diff --git a/src/ast.rs b/src/ast.rs deleted file mode 100644 index f659909..0000000 --- a/src/ast.rs +++ /dev/null @@ -1,195 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum AstNode { - // Primitives - Integer(i32), - Identifier(String), - String(String), - Boolean(bool), - // Unary operators - Not { - operand: Box, - }, - // Infix operators - Equal { - left: Box, - right: Box, - }, - Add { - left: Box, - right: Box, - }, - Subtract { - left: Box, - right: Box, - }, - Multiply { - left: Box, - right: Box, - }, - Divide { - left: Box, - right: Box, - }, - Assign { - left: Box, - right: Box, - }, - // Control flow - Block { - statements: Vec, - }, - If { - condition: Box, - consequence: Box, - alternative: Option>, - }, - While { - condition: Box, - body: Box, - }, - // Functions and variables - FunctionCall { - identifier: Box, - arguments: Vec, - }, - FunctionReturn { - value: Box, - }, - FunctionDefinition { - identifier: Box, - arguments: Vec, - body: Box, - }, - VariableDeclaration { - identifier: Box, - }, - // Other - Import { - identifier: Box, - }, - Null, -} -impl AstNode { - // Primitives - pub fn integer(value: i32) -> AstNode { - AstNode::Integer(value) - } - pub fn identifier(value: String) -> AstNode { - AstNode::Identifier(value) - } - pub fn string(value: String) -> AstNode { - AstNode::String(value) - } - pub fn boolean(value: bool) -> AstNode { - AstNode::Boolean(value) - } - // Unary operators - pub fn not(operand: AstNode) -> AstNode { - AstNode::Not { - operand: Box::new(operand), - } - } - // Infix operators - pub fn equal(left: AstNode, right: AstNode) -> AstNode { - AstNode::Equal { - left: Box::new(left), - right: Box::new(right), - } - } - pub fn not_equal(left: AstNode, right: AstNode) -> AstNode { - AstNode::not(AstNode::Equal { - left: Box::new(left), - right: Box::new(right), - }) - } - pub fn add(left: AstNode, right: AstNode) -> AstNode { - AstNode::Add { - left: Box::new(left), - right: Box::new(right), - } - } - pub fn subtract(left: AstNode, right: AstNode) -> AstNode { - AstNode::Subtract { - left: Box::new(left), - right: Box::new(right), - } - } - pub fn multiply(left: AstNode, right: AstNode) -> AstNode { - AstNode::Multiply { - left: Box::new(left), - right: Box::new(right), - } - } - pub fn divide(left: AstNode, right: AstNode) -> AstNode { - AstNode::Divide { - left: Box::new(left), - right: Box::new(right), - } - } - pub fn assign(left: AstNode, right: AstNode) -> AstNode { - AstNode::Assign { - left: Box::new(left), - right: Box::new(right), - } - } - // Control flow - pub fn block(statements: Vec) -> AstNode { - AstNode::Block { statements } - } - pub fn if_statement( - condition: AstNode, - consequence: AstNode, - alternative: Option, - ) -> AstNode { - AstNode::If { - condition: Box::new(condition), - consequence: Box::new(consequence), - alternative: alternative.and_then(|alt| Some(Box::new(alt))), - } - } - pub fn while_loop(condition: AstNode, body: AstNode) -> AstNode { - AstNode::While { - condition: Box::new(condition), - body: Box::new(body), - } - } - // Functions and variables - pub fn function_call(identifier: AstNode, arguments: Vec) -> AstNode { - AstNode::FunctionCall { - identifier: Box::new(identifier), - arguments, - } - } - pub fn function_return(value: AstNode) -> AstNode { - AstNode::FunctionReturn { - value: Box::new(value), - } - } - pub fn function_definition( - identifier: AstNode, - arguments: Vec, - body: AstNode, - ) -> AstNode { - AstNode::FunctionDefinition { - identifier: Box::new(identifier), - arguments, - body: Box::new(body), - } - } - pub fn variable_declaration(identifier: AstNode) -> AstNode { - AstNode::VariableDeclaration { - identifier: Box::new(identifier), - } - } - // Other - pub fn import(identifier: AstNode) -> AstNode { - AstNode::Import { - identifier: Box::new(identifier), - } - } - pub fn null() -> AstNode { - AstNode::Null - } -} diff --git a/src/codegen.rs b/src/codegen.rs index 9c578a8..cf05a6c 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,185 +1,5 @@ -use crate::ast::{AstNode, AstNodeKind}; -use ron::from_str; +use crate::{parse::AstNode, InterpreterError}; -pub struct SymbolGenerator { - counter: usize, -} -impl SymbolGenerator { - pub fn new() -> SymbolGenerator { - SymbolGenerator { counter: 0 } - } - pub fn next(&mut self) -> usize { - self.counter += 1; - self.counter - } -} - -pub trait Wasm { - fn emit(&self, symbol_generator: &mut SymbolGenerator) -> String; -} -impl Wasm for AstNode { - fn emit(&self, s: &mut SymbolGenerator) -> String { - use AstNodeKind::*; - match self.kind { - // Primitives - Integer => format!("(i32.const {})", self.value), - Identifier => format!("(get_local ${})", self.value), - // Unary operators - Not => format!("(i32.eq (i32.const 0) {})", self.subnodes[1].emit(s)), - // Infix operators - NotEqual => format!( - "(i32.ne {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - Equal => format!( - "(i32.eq {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - Add => format!( - "(i32.add {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - Subtract => format!( - "(i32.sub {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - Multiply => format!( - "(i32.mul {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - Divide => format!( - "(i32.div_s {} {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ), - // Control flow - Block => { - let mut out = String::new(); - for node in &self.subnodes { - out += ""; - out += &node.emit(s); - } - out - } - IfStatement => { - let mut out = String::new(); - out += &format!( - "(if {} (then {})", - self.subnodes[0].emit(s), - self.subnodes[1].emit(s) - ); // Emit the conditional and consequence. - if let Some(alternative) = self.subnodes.get(2) { - out += &format!(" (else {})", alternative.emit(s)); // Emit the alternative. - } - out += ")"; - out - } - WhileLoop => { - let loop_symbol = format!("while{}", s.next()); // TODO: Make generate unique symbol for nested loops. - let mut out = String::new(); - out += &format!("(block ${}_wrapper", loop_symbol); - out += &format!(" (loop ${}_loop", loop_symbol); - out += &format!(" {}", self.subnodes[1].emit(s)); - out += &format!( - " (br_if ${}_wrapper (i32.eq (i32.const 0) {}))", - loop_symbol, - self.subnodes[0].emit(s) - ); - out += &format!(" (br ${}_loop)", loop_symbol); - out += "))"; - out - } - Program => { - let mut out = String::new(); - out += "(module"; - let mut exported = vec![]; - for node in &self.subnodes { - out += " "; - out += &node.emit(s); - if node.kind == FunctionDefinition { - exported.push(node.value.clone()); - } - } - for export in exported { - out += &format!(" (export \"{0}\" (func ${0}))", export); - } - out += ")"; - out - } - // Functions and variables - FunctionCall => { - let mut out = String::new(); - out += &format!("(call ${}", from_str::(&self.value).unwrap().value); - for n in &self.subnodes { - out += " "; - out += &n.emit(s); - } - out += ")"; - out - } - FunctionReturn => format!("{} (return)", self.subnodes[0].emit(s)), - FunctionDefinition => { - let mut out = String::new(); - out += &format!("(func ${}", self.value); - let body = self.subnodes[0].clone(); - for n in &self.subnodes[1..] { - out += &format!(" (param ${} i32)", n.value); - } - let mut func_returns_value = false; - let mut index = 0; - loop { - if index >= body.subnodes.len() { - break; - } - match body.subnodes[index].kind { - AstNodeKind::FunctionReturn => func_returns_value = true, - _ => {} - } - index += 1; - } - if func_returns_value { - out += " (result i32)"; - } - for n in &body.subnodes { - out += " "; - out += &n.emit(s); - } - out += ")"; - out - } - VariableDeclaration => format!("(local ${} i32)", self.value), - Assign => format!("(set_local ${} {})", self.value, self.subnodes[0].emit(s)), - // Import - Import => { - r#"(import "console" "log" (func $log (param i32)))"#.into() - // let mut out = String::new(); - // out += "(import"; - // let num_args = self.subnodes[0].clone().value.parse::().unwrap(); - // let returns_value = self.subnodes[1].clone().value.parse::().unwrap() > 0; - // // (import "console" "log" (func $log (param i32))) - // out += &format!(" \"{}\"", self.subnodes[2].value.clone()); - // let mut combined_name = self.subnodes[2].value.clone(); - // for path in self.subnodes[3..].iter() { - // out += &format!(" \"{}\"", path.value.clone()); - // combined_name += &format!("_{}", path.value.clone()); - // } - // out += &format!(" (func ${}", combined_name); - // for _ in 0..num_args { - // out += " (param i32)"; - // } - // if returns_value { - // out += " (result i32)"; - // } - // out += "))"; - // out - } - // Blank node / other - Null | _ => "".into(), - } - } +pub fn codegen(_ast: &AstNode) -> Result { + Ok(String::new()) } diff --git a/src/lib.rs b/src/lib.rs index ff4fdb0..8916bf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,32 @@ -pub mod ast; -// pub mod codegen; +pub mod codegen; pub mod parse; +pub mod tokenize; -// use codegen::{SymbolGenerator, Wasm}; - -pub fn compile(source: &str) -> Vec { - wat::parse_str(compile_wat(source)).unwrap() +#[derive(Debug)] +pub enum InterpreterError { + /// Error parsing source + ParseError(String), + /// Unexpected token + UnexpectedToken, + /// Mismatched types + MismatchedTypes, + /// Type error + TypeError, + /// Unexpected EOF + UnexpectedEOF, + /// Expected value + ExpectedValue, + /// Unimplemented + Unimplemented, +} +impl From> for InterpreterError { + fn from(_value: Option) -> InterpreterError { + InterpreterError::ExpectedValue + } } -pub fn compile_wat(source: &str) -> String { - // let mut s = SymbolGenerator::new(); - let ast = parse::run(source); - // println!("{:?}", ast); - unimplemented!() - // let wasm = ast.emit(&mut s); - // println!("{}", wasm); - // wasm +pub fn compile(source: &str) -> Result { + let tokens = tokenize::tokenize(source); + let ast = parse::parse(&tokens?); + codegen::codegen(&ast?) } diff --git a/src/main.rs b/src/main.rs index b32e4f6..f811286 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,10 @@ -use std::fs::File; -use std::io::prelude::*; +use std::{fs::File, io::Write}; fn main() -> std::io::Result<()> { // Read the source from a file. let source = std::fs::read_to_string("test.pvt").unwrap(); - // Compile it - let _value = pivot::parse::run(&source); - // let binary = pivot::compile(&source); + let code = pivot::compile(&source).unwrap(); // Write it to a file. - // File::create("out.wasm")?.write_all(&binary)?; + File::create("out.bf")?.write_all(code.as_bytes())?; Ok(()) } diff --git a/src/parse.rs b/src/parse.rs index 8614e84..6be5613 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,190 +1,4 @@ -#[derive(Clone, PartialEq, Debug)] -pub enum Token { - Integer(i64), - Float(f64), - String(String), - Boolean(bool), - Identifier(String), - Keyword(String), - Comma, - Plus, - Minus, - Star, - Slash, - Bang, - Equals, - Semicolon, - Quote(char), - Parenthesis { closing: bool }, - Whitespace(String), - Unknown, -} - -#[derive(Debug)] -pub enum InterpreterError { - /// Error parsing source - ParseError(String), - /// Unexpected token - UnexpectedToken, - /// Mismatched types - MismatchedTypes, - /// Type error - TypeError, - /// Unexpected EOF - UnexpectedEOF, - /// Expected value - ExpectedValue, -} -impl From> for InterpreterError { - fn from(value: Option) -> InterpreterError { - InterpreterError::ExpectedValue - } -} - -pub fn tokenize(source: &str) -> Result, InterpreterError> { - fn tokenize_number(chars: &[char]) -> Result<(Token, usize), ()> { - let mut current_value = String::new(); - let mut chars_consumed = 0; - for c in chars { - if !c.is_digit(10) && *c != '.' { - break; - } - current_value.push(*c); - chars_consumed += 1; - } - if chars_consumed == 0 { - Err(()) - } else if current_value.contains(".") { - Ok(( - Token::Float(current_value.parse::().unwrap()), - chars_consumed, - )) - } else { - Ok(( - Token::Integer(current_value.parse::().unwrap()), - chars_consumed, - )) - } - } - fn tokenize_identifier(chars: &[char]) -> Result<(Token, usize), ()> { - let mut current_value = String::new(); - let mut chars_consumed = 0; - if chars[chars_consumed].is_alphabetic() { - current_value.push(chars[chars_consumed]); - } else { - return Err(()); - } - chars_consumed += 1; - while chars_consumed < chars.len() - && (chars[chars_consumed].is_alphanumeric() || chars[chars_consumed] == '_') - { - current_value.push(chars[chars_consumed]); - chars_consumed += 1; - } - match ¤t_value[..] { - "true" => Ok((Token::Boolean(true), 4)), - "false" => Ok((Token::Boolean(false), 5)), - "let" => Ok((Token::Keyword(current_value), chars_consumed)), - _ => Ok((Token::Identifier(current_value), chars_consumed)), - } - } - fn tokenize_string(chars: &[char]) -> Result<(Token, usize), ()> { - let start_quote; - let mut current_value = String::new(); - let mut chars_consumed = 0; - fn is_quote(c: char) -> bool { - match c { - '\'' | '"' | '`' => true, - _ => false, - } - } - if is_quote(chars[chars_consumed]) { - start_quote = chars[chars_consumed]; - } else { - return Err(()); - } - chars_consumed += 1; - while chars_consumed < chars.len() && chars[chars_consumed] != start_quote { - current_value.push(chars[chars_consumed]); - chars_consumed += 1; - } - chars_consumed += 1; - Ok((Token::String(current_value), chars_consumed)) - } - fn tokenize_whitespace(chars: &[char]) -> Result<(Token, usize), ()> { - let mut current_value = String::new(); - let mut chars_consumed = 0; - for c in chars { - if !c.is_whitespace() { - break; - } - chars_consumed += 1; - current_value.push(*c); - } - if chars_consumed == 0 { - Err(()) - } else { - Ok((Token::Whitespace(current_value), chars_consumed)) - } - } - fn tokenize_operator(chars: &[char]) -> Result<(Token, usize), ()> { - if chars.is_empty() { - Err(()) - } else if chars[0] == '+' { - Ok((Token::Plus, 1)) - } else if chars[0] == '-' { - Ok((Token::Minus, 1)) - } else if chars[0] == '*' { - Ok((Token::Star, 1)) - } else if chars[0] == '/' { - Ok((Token::Slash, 1)) - } else if chars[0] == '!' { - Ok((Token::Bang, 1)) - } else if chars[0] == '=' { - Ok((Token::Equals, 1)) - } else { - Err(()) - } - } - - let source = source.chars().collect::>(); - let mut tokens = vec![]; - let mut index = 0; - while index < source.len() { - if let Ok((_whitespace, chars_consumed)) = tokenize_whitespace(&source[index..]) { - // Ignore whitespace - index += chars_consumed; - } else if let Ok((num, chars_consumed)) = tokenize_number(&source[index..]) { - tokens.push(num); - index += chars_consumed; - } else if let Ok((num, chars_consumed)) = tokenize_string(&source[index..]) { - tokens.push(num); - index += chars_consumed; - } else if let Ok((num, chars_consumed)) = tokenize_identifier(&source[index..]) { - tokens.push(num); - index += chars_consumed; - } else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) { - tokens.push(operator); - index += chars_consumed; - } else if source[index] == ',' { - tokens.push(Token::Comma); - index += 1; - } else if source[index] == ';' { - tokens.push(Token::Semicolon); - index += 1; - } else if source[index] == '(' { - tokens.push(Token::Parenthesis { closing: false }); - index += 1; - } else if source[index] == ')' { - tokens.push(Token::Parenthesis { closing: true }); - index += 1; - } else { - // Skip if things fail - index += 1; - } - } - Ok(tokens) -} +use crate::{tokenize::Token, InterpreterError}; #[derive(Clone, PartialEq, Debug)] pub enum AstPrimitive { @@ -192,9 +6,9 @@ pub enum AstPrimitive { Float(f64), String(String), Boolean(bool), - Identifier(String), Null, } +#[allow(clippy::derive_hash_xor_eq)] impl std::hash::Hash for AstPrimitive { fn hash(&self, state: &mut H) { match self { @@ -203,10 +17,23 @@ impl std::hash::Hash for AstPrimitive { } } } +impl std::fmt::Display for AstPrimitive { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + use AstPrimitive::*; + match self { + Integer(n) => write!(f, "{}", n), + Float(n) => write!(f, "{}", n), + String(s) => write!(f, "{}", s), + Boolean(b) => write!(f, "{}", b), + Null => write!(f, "null"), + } + } +} #[derive(Clone, PartialEq, Debug)] pub enum AstNode { Primitive(AstPrimitive), + Identifier(String), Negate { body: Box, }, @@ -233,6 +60,10 @@ pub enum AstNode { left: Box, right: Box, }, + FunctionCall { + identifier: String, + arguments: Vec, + }, Program { statements: Vec, }, @@ -240,6 +71,62 @@ pub enum AstNode { } pub fn parse(tokens: &[Token]) -> Result { + fn parse_function_call(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + let identifier; + if let Token::Identifier(id) = tokens[index].clone() { + identifier = id; + index += 1; + } else { + return Err(InterpreterError::UnexpectedToken); + } + if !matches!(tokens[index], Token::Parenthesis { closing: false }) { + return Ok((AstNode::Identifier(identifier), 1)); + } else { + index += 1; + } + // Check if it closes right away. + if matches!(tokens[index], Token::Parenthesis { closing: true }) { + index += 1; + return Ok(( + AstNode::FunctionCall { + identifier, + arguments: vec![], + }, + index, + )); + } + + let mut arguments = vec![]; + if let Ok((argument, tokens_consumed)) = parse_expression(&tokens[index..]) { + arguments.push(argument); + index += tokens_consumed; + } + while index + 2 < tokens.len() { + if tokens[index] != Token::Comma { + break; + } + index += 1; + if let Ok((argument, tokens_consumed)) = parse_expression(&tokens[index..]) { + arguments.push(argument); + index += tokens_consumed; + } else { + break; + } + } + if matches!(tokens[index], Token::Parenthesis { closing: true }) { + index += 1; + Ok(( + AstNode::FunctionCall { + identifier, + arguments, + }, + index, + )) + } else { + Err(InterpreterError::UnexpectedToken) + } + } fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { if tokens.is_empty() { Err(InterpreterError::UnexpectedEOF) @@ -251,8 +138,8 @@ pub fn parse(tokens: &[Token]) -> Result { Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1)) } else if let Token::String(s) = &tokens[0] { Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1)) - } else if let Token::Identifier(s) = &tokens[0] { - Ok((AstNode::Primitive(AstPrimitive::Identifier(s.clone())), 1)) + } else if let Token::Identifier(_) = &tokens[0] { + parse_function_call(tokens) } else if tokens[0] == Token::Keyword("let".to_owned()) { if tokens.len() < 2 { Err(InterpreterError::UnexpectedEOF) @@ -294,7 +181,7 @@ pub fn parse(tokens: &[Token]) -> Result { fn parse_unary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { let mut index = 0; if tokens[index] != Token::Minus && tokens[index] != Token::Bang { - return parse_grouped_expression(&tokens[index..]); + parse_grouped_expression(&tokens[index..]) } else { let operation = tokens[index].clone(); index += 1; @@ -396,180 +283,9 @@ pub fn parse(tokens: &[Token]) -> Result { if index >= tokens.len() { break; } - let statement = parse_expression(&tokens[index..]); - if let Ok((statement, tokens_consumed)) = statement { - statements.push(statement); - index += tokens_consumed; - } else { - break; - } + let (statement, tokens_consumed) = parse_expression(&tokens[index..])?; + statements.push(statement); + index += tokens_consumed; } Ok(AstNode::Program { statements }) } -pub fn interpret(ast: &AstNode) -> Result<(), InterpreterError> { - use std::{collections::HashMap, mem::discriminant}; - use AstNode::*; - let mut vars: HashMap> = HashMap::new(); - if let Program { statements } = ast { - for statement in statements { - let _ = interpret_statement(statement, &mut vars)?; - } - } - fn interpret_statement( - ast: &AstNode, - vars: &mut HashMap>, - ) -> Result, InterpreterError> { - match ast { - Primitive(p) => { - if let AstPrimitive::Identifier(id) = p { - if let Some(val) = vars.get(id) { - if let Some(val) = val { - Ok(Some(val.clone())) - } else { - Err(InterpreterError::ParseError( - "Variable used before definition".to_owned(), - )) - } - } else { - Err(InterpreterError::ParseError( - "Variable used before declaration".to_owned(), - )) - } - } else { - Ok(Some(p.clone())) - } - } - Negate { body } => { - if let Some(AstPrimitive::Integer(body)) = interpret_statement(body, vars)? { - Ok(Some(AstPrimitive::Integer(body * -1))) - } else if let Some(AstPrimitive::Boolean(body)) = interpret_statement(body, vars)? { - Ok(Some(AstPrimitive::Boolean(!body))) - } else { - Err(InterpreterError::TypeError) - } - } - Declare { identifier } => { - vars.insert(identifier.clone(), None); - Ok(None) - } - Assign { left, right } => { - if let AstNode::Declare { identifier } = Box::leak(left.clone()) { - let _ = interpret(left)?; - let value = interpret_statement(right, vars)?; - vars.insert(identifier.clone(), value); - Ok(Some(vars.get(identifier).unwrap().clone().unwrap().clone())) - } else if let AstNode::Primitive(AstPrimitive::Identifier(id)) = - Box::leak(left.clone()) - { - let id = id.clone(); - let value = interpret_statement(right, vars)?; - vars.insert(id.clone(), value); - Ok(Some(vars.get(&id).unwrap().clone().unwrap().clone())) - } else { - Err(InterpreterError::TypeError) - } - } - Add { left, right } => { - let left = - interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; - let right = - interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; - if discriminant(&left) != discriminant(&right) { - Err(InterpreterError::MismatchedTypes) - } else { - if let AstPrimitive::Integer(left) = left { - if let AstPrimitive::Integer(right) = right { - return Ok(Some(AstPrimitive::Integer(left + right))); - } - } - if let AstPrimitive::Float(left) = left { - if let AstPrimitive::Float(right) = right { - return Ok(Some(AstPrimitive::Float(left + right))); - } - } - if let AstPrimitive::String(left) = left { - if let AstPrimitive::String(right) = right { - return Ok(Some(AstPrimitive::String(format!("{}{}", left, right)))); - } - } - Err(InterpreterError::TypeError) - } - } - Subtract { left, right } => { - let left = - interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; - let right = - interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; - if discriminant(&left) != discriminant(&right) { - Err(InterpreterError::MismatchedTypes) - } else { - if let AstPrimitive::Integer(left) = left { - if let AstPrimitive::Integer(right) = right { - return Ok(Some(AstPrimitive::Integer(left - right))); - } - } - if let AstPrimitive::Float(left) = left { - if let AstPrimitive::Float(right) = right { - return Ok(Some(AstPrimitive::Float(left - right))); - } - } - Err(InterpreterError::TypeError) - } - } - Multiply { left, right } => { - let left = - interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; - let right = - interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; - if discriminant(&left) != discriminant(&right) { - Err(InterpreterError::MismatchedTypes) - } else { - if let AstPrimitive::Integer(left) = left { - if let AstPrimitive::Integer(right) = right { - return Ok(Some(AstPrimitive::Integer(left * right))); - } - } - if let AstPrimitive::Float(left) = left { - if let AstPrimitive::Float(right) = right { - return Ok(Some(AstPrimitive::Float(left * right))); - } - } - Err(InterpreterError::TypeError) - } - } - Divide { left, right } => { - let left = - interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; - let right = - interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; - if discriminant(&left) != discriminant(&right) { - Err(InterpreterError::MismatchedTypes) - } else { - if let AstPrimitive::Integer(left) = left { - if let AstPrimitive::Integer(right) = right { - return Ok(Some(AstPrimitive::Integer(left / right))); - } - } - if let AstPrimitive::Float(left) = left { - if let AstPrimitive::Float(right) = right { - return Ok(Some(AstPrimitive::Float(left / right))); - } - } - Err(InterpreterError::TypeError) - } - } - _ => Err(InterpreterError::TypeError), - } - } - Ok(()) -} -pub fn run(source: &str) -> Result<(), InterpreterError> { - println!("source: {:?}", source); - let tokens = tokenize(source); - println!("tokens: {:?}", tokens); - let ast = parse(&tokens?); - println!("ast: {:?}", ast); - let value = interpret(&ast?); - println!("value: {:?}", value); - Ok(()) -} diff --git a/src/tokenize.rs b/src/tokenize.rs new file mode 100644 index 0000000..951f0d1 --- /dev/null +++ b/src/tokenize.rs @@ -0,0 +1,164 @@ +use crate::InterpreterError; + +#[derive(Clone, PartialEq, Debug)] +pub enum Token { + Integer(i64), + Float(f64), + String(String), + Boolean(bool), + Identifier(String), + Keyword(String), + Comma, + Plus, + Minus, + Star, + Slash, + Bang, + Equals, + Semicolon, + Quote(char), + Parenthesis { closing: bool }, + Whitespace(String), + Unknown, +} + +pub fn tokenize(source: &str) -> Result, InterpreterError> { + fn tokenize_number(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + for c in chars { + if !c.is_digit(10) && *c != '.' { + break; + } + current_value.push(*c); + chars_consumed += 1; + } + if chars_consumed == 0 { + Err(()) + } else if current_value.contains('.') { + Ok(( + Token::Float(current_value.parse::().unwrap()), + chars_consumed, + )) + } else { + Ok(( + Token::Integer(current_value.parse::().unwrap()), + chars_consumed, + )) + } + } + fn tokenize_identifier(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + if chars[chars_consumed].is_alphabetic() { + current_value.push(chars[chars_consumed]); + } else { + return Err(()); + } + chars_consumed += 1; + while chars_consumed < chars.len() + && (chars[chars_consumed].is_alphanumeric() || chars[chars_consumed] == '_') + { + current_value.push(chars[chars_consumed]); + chars_consumed += 1; + } + match ¤t_value[..] { + "true" => Ok((Token::Boolean(true), 4)), + "false" => Ok((Token::Boolean(false), 5)), + "let" => Ok((Token::Keyword(current_value), chars_consumed)), + _ => Ok((Token::Identifier(current_value), chars_consumed)), + } + } + fn tokenize_string(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + fn is_quote(c: char) -> bool { + matches!(c, '\'' | '"' | '`') + } + let start_quote = if is_quote(chars[chars_consumed]) { + chars[chars_consumed] + } else { + return Err(()); + }; + chars_consumed += 1; + while chars_consumed < chars.len() && chars[chars_consumed] != start_quote { + current_value.push(chars[chars_consumed]); + chars_consumed += 1; + } + chars_consumed += 1; + Ok((Token::String(current_value), chars_consumed)) + } + fn tokenize_whitespace(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + for c in chars { + if !c.is_whitespace() { + break; + } + chars_consumed += 1; + current_value.push(*c); + } + if chars_consumed == 0 { + Err(()) + } else { + Ok((Token::Whitespace(current_value), chars_consumed)) + } + } + fn tokenize_operator(chars: &[char]) -> Result<(Token, usize), ()> { + if chars.is_empty() { + Err(()) + } else if chars[0] == '+' { + Ok((Token::Plus, 1)) + } else if chars[0] == '-' { + Ok((Token::Minus, 1)) + } else if chars[0] == '*' { + Ok((Token::Star, 1)) + } else if chars[0] == '/' { + Ok((Token::Slash, 1)) + } else if chars[0] == '!' { + Ok((Token::Bang, 1)) + } else if chars[0] == '=' { + Ok((Token::Equals, 1)) + } else { + Err(()) + } + } + + let source = source.chars().collect::>(); + let mut tokens = vec![]; + let mut index = 0; + while index < source.len() { + if let Ok((_whitespace, chars_consumed)) = tokenize_whitespace(&source[index..]) { + // Ignore whitespace + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_number(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_string(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_identifier(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) { + tokens.push(operator); + index += chars_consumed; + } else if source[index] == ',' { + tokens.push(Token::Comma); + index += 1; + } else if source[index] == ';' { + tokens.push(Token::Semicolon); + index += 1; + } else if source[index] == '(' { + tokens.push(Token::Parenthesis { closing: false }); + index += 1; + } else if source[index] == ')' { + tokens.push(Token::Parenthesis { closing: true }); + index += 1; + } else { + // Skip if things fail + index += 1; + } + } + Ok(tokens) +} diff --git a/test.js b/test.js deleted file mode 100644 index 293b306..0000000 --- a/test.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; -const fs = require('fs'); -const WASI = require('wasi'); -const wasi = new WASI({ - args: process.argv, - env: process.env, - preopens: { - '/sandbox': __dirname - } -}); -const importObject = { - wasi_snapshot_preview1: wasi.wasiImport, - console, -}; - -(async () => { - const wasm = await WebAssembly.compile(fs.readFileSync('./out.wasm')); - const instance = await WebAssembly.instantiate(wasm, importObject); - - instance.exports.main(); -})(); diff --git a/test.pvt b/test.pvt index eac2de5..9ab73fd 100644 --- a/test.pvt +++ b/test.pvt @@ -1,2 +1 @@ -let asdf = 4 -asdf + 1 \ No newline at end of file +log("Hello world!") \ No newline at end of file diff --git a/test.txt b/test.txt deleted file mode 100644 index b3985f2..0000000 --- a/test.txt +++ /dev/null @@ -1,15 +0,0 @@ -PrimaryExpression = Identifier | Literal -GroupedExpression = PrimaryExpression | `(` Expression `)` - -UnaryOperator = `-` | `!` -UnaryExpression = GroupedExpression | UnaryOperator UnaryExpression - -MultiplyOperator = `*` | `/` -MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator UnaryExpression - -AddOperator = `+` | `-` -AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression - -AssignExpression = AddExpression | Identifier `=` AddExpression - -Expression = AssignExpression \ No newline at end of file