Remove wasm
This commit is contained in:
parent
0be64e5eb3
commit
2f1cfdb329
112
Cargo.lock
generated
112
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -1,14 +1,8 @@
|
||||
[package]
|
||||
name = "pivot"
|
||||
version = "0.1.0"
|
||||
authors = ["Garen Tyler <garentyler@gmail.com>"]
|
||||
authors = ["Garen Tyler <garentyler@garen.dev>"]
|
||||
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"
|
29
package-lock.json
generated
29
package-lock.json
generated
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
package.json
27
package.json
@ -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"
|
||||
}
|
195
src/ast.rs
195
src/ast.rs
@ -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<AstNode>,
|
||||
},
|
||||
// Infix operators
|
||||
Equal {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
Add {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
Subtract {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
Multiply {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
Divide {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
Assign {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
// Control flow
|
||||
Block {
|
||||
statements: Vec<AstNode>,
|
||||
},
|
||||
If {
|
||||
condition: Box<AstNode>,
|
||||
consequence: Box<AstNode>,
|
||||
alternative: Option<Box<AstNode>>,
|
||||
},
|
||||
While {
|
||||
condition: Box<AstNode>,
|
||||
body: Box<AstNode>,
|
||||
},
|
||||
// Functions and variables
|
||||
FunctionCall {
|
||||
identifier: Box<AstNode>,
|
||||
arguments: Vec<AstNode>,
|
||||
},
|
||||
FunctionReturn {
|
||||
value: Box<AstNode>,
|
||||
},
|
||||
FunctionDefinition {
|
||||
identifier: Box<AstNode>,
|
||||
arguments: Vec<AstNode>,
|
||||
body: Box<AstNode>,
|
||||
},
|
||||
VariableDeclaration {
|
||||
identifier: Box<AstNode>,
|
||||
},
|
||||
// Other
|
||||
Import {
|
||||
identifier: Box<AstNode>,
|
||||
},
|
||||
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 {
|
||||
AstNode::Block { statements }
|
||||
}
|
||||
pub fn if_statement(
|
||||
condition: AstNode,
|
||||
consequence: AstNode,
|
||||
alternative: Option<AstNode>,
|
||||
) -> 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 {
|
||||
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<AstNode>,
|
||||
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
|
||||
}
|
||||
}
|
186
src/codegen.rs
186
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::<AstNode>(&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::<u32>().unwrap();
|
||||
// let returns_value = self.subnodes[1].clone().value.parse::<u32>().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<String, InterpreterError> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
41
src/lib.rs
41
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<u8> {
|
||||
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<T> From<Option<T>> for InterpreterError {
|
||||
fn from(_value: Option<T>) -> 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<String, InterpreterError> {
|
||||
let tokens = tokenize::tokenize(source);
|
||||
let ast = parse::parse(&tokens?);
|
||||
codegen::codegen(&ast?)
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
446
src/parse.rs
446
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<T> From<Option<T>> for InterpreterError {
|
||||
fn from(value: Option<T>) -> InterpreterError {
|
||||
InterpreterError::ExpectedValue
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tokenize(source: &str) -> Result<Vec<Token>, 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::<f64>().unwrap()),
|
||||
chars_consumed,
|
||||
))
|
||||
} else {
|
||||
Ok((
|
||||
Token::Integer(current_value.parse::<i64>().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::<Vec<char>>();
|
||||
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<H: std::hash::Hasher>(&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<AstNode>,
|
||||
},
|
||||
@ -233,6 +60,10 @@ pub enum AstNode {
|
||||
left: Box<AstNode>,
|
||||
right: Box<AstNode>,
|
||||
},
|
||||
FunctionCall {
|
||||
identifier: String,
|
||||
arguments: Vec<AstNode>,
|
||||
},
|
||||
Program {
|
||||
statements: Vec<AstNode>,
|
||||
},
|
||||
@ -240,6 +71,62 @@ pub enum AstNode {
|
||||
}
|
||||
|
||||
pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
|
||||
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<AstNode, InterpreterError> {
|
||||
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<AstNode, InterpreterError> {
|
||||
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<AstNode, InterpreterError> {
|
||||
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<String, Option<AstPrimitive>> = 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<String, Option<AstPrimitive>>,
|
||||
) -> Result<Option<AstPrimitive>, 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(())
|
||||
}
|
||||
|
164
src/tokenize.rs
Normal file
164
src/tokenize.rs
Normal file
@ -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<Vec<Token>, 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::<f64>().unwrap()),
|
||||
chars_consumed,
|
||||
))
|
||||
} else {
|
||||
Ok((
|
||||
Token::Integer(current_value.parse::<i64>().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::<Vec<char>>();
|
||||
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)
|
||||
}
|
21
test.js
21
test.js
@ -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();
|
||||
})();
|
15
test.txt
15
test.txt
@ -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
|
Loading…
x
Reference in New Issue
Block a user