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.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "pivot"
|
name = "pivot"
|
||||||
version = "0.1.0"
|
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]
|
[package]
|
||||||
name = "pivot"
|
name = "pivot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Garen Tyler <garentyler@gmail.com>"]
|
authors = ["Garen Tyler <garentyler@garen.dev>"]
|
||||||
edition = "2018"
|
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"
|
license = "MIT"
|
||||||
repository = "https://github.com/garentyler/pivot"
|
repository = "https://github.com/garentyler/pivot"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ron = "0.6.2"
|
|
||||||
serde = "1.0.117"
|
|
||||||
wat = "1.0.29"
|
|
||||||
nyst = { path = "../nyst" }
|
|
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 crate::{parse::AstNode, InterpreterError};
|
||||||
use ron::from_str;
|
|
||||||
|
|
||||||
pub struct SymbolGenerator {
|
pub fn codegen(_ast: &AstNode) -> Result<String, InterpreterError> {
|
||||||
counter: usize,
|
Ok(String::new())
|
||||||
}
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 parse;
|
||||||
|
pub mod tokenize;
|
||||||
|
|
||||||
// use codegen::{SymbolGenerator, Wasm};
|
#[derive(Debug)]
|
||||||
|
pub enum InterpreterError {
|
||||||
pub fn compile(source: &str) -> Vec<u8> {
|
/// Error parsing source
|
||||||
wat::parse_str(compile_wat(source)).unwrap()
|
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 {
|
pub fn compile(source: &str) -> Result<String, InterpreterError> {
|
||||||
// let mut s = SymbolGenerator::new();
|
let tokens = tokenize::tokenize(source);
|
||||||
let ast = parse::run(source);
|
let ast = parse::parse(&tokens?);
|
||||||
// println!("{:?}", ast);
|
codegen::codegen(&ast?)
|
||||||
unimplemented!()
|
|
||||||
// let wasm = ast.emit(&mut s);
|
|
||||||
// println!("{}", wasm);
|
|
||||||
// wasm
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
use std::fs::File;
|
use std::{fs::File, io::Write};
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
// Read the source from a file.
|
// Read the source from a file.
|
||||||
let source = std::fs::read_to_string("test.pvt").unwrap();
|
let source = std::fs::read_to_string("test.pvt").unwrap();
|
||||||
// Compile it
|
let code = pivot::compile(&source).unwrap();
|
||||||
let _value = pivot::parse::run(&source);
|
|
||||||
// let binary = pivot::compile(&source);
|
|
||||||
// Write it to a file.
|
// Write it to a file.
|
||||||
// File::create("out.wasm")?.write_all(&binary)?;
|
File::create("out.bf")?.write_all(code.as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
446
src/parse.rs
446
src/parse.rs
@ -1,190 +1,4 @@
|
|||||||
#[derive(Clone, PartialEq, Debug)]
|
use crate::{tokenize::Token, InterpreterError};
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum AstPrimitive {
|
pub enum AstPrimitive {
|
||||||
@ -192,9 +6,9 @@ pub enum AstPrimitive {
|
|||||||
Float(f64),
|
Float(f64),
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Identifier(String),
|
|
||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
impl std::hash::Hash for AstPrimitive {
|
impl std::hash::Hash for AstPrimitive {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
match self {
|
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)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum AstNode {
|
pub enum AstNode {
|
||||||
Primitive(AstPrimitive),
|
Primitive(AstPrimitive),
|
||||||
|
Identifier(String),
|
||||||
Negate {
|
Negate {
|
||||||
body: Box<AstNode>,
|
body: Box<AstNode>,
|
||||||
},
|
},
|
||||||
@ -233,6 +60,10 @@ pub enum AstNode {
|
|||||||
left: Box<AstNode>,
|
left: Box<AstNode>,
|
||||||
right: Box<AstNode>,
|
right: Box<AstNode>,
|
||||||
},
|
},
|
||||||
|
FunctionCall {
|
||||||
|
identifier: String,
|
||||||
|
arguments: Vec<AstNode>,
|
||||||
|
},
|
||||||
Program {
|
Program {
|
||||||
statements: Vec<AstNode>,
|
statements: Vec<AstNode>,
|
||||||
},
|
},
|
||||||
@ -240,6 +71,62 @@ pub enum AstNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
|
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> {
|
fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
if tokens.is_empty() {
|
if tokens.is_empty() {
|
||||||
Err(InterpreterError::UnexpectedEOF)
|
Err(InterpreterError::UnexpectedEOF)
|
||||||
@ -251,8 +138,8 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
|
|||||||
Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1))
|
Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1))
|
||||||
} else if let Token::String(s) = &tokens[0] {
|
} else if let Token::String(s) = &tokens[0] {
|
||||||
Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1))
|
Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1))
|
||||||
} else if let Token::Identifier(s) = &tokens[0] {
|
} else if let Token::Identifier(_) = &tokens[0] {
|
||||||
Ok((AstNode::Primitive(AstPrimitive::Identifier(s.clone())), 1))
|
parse_function_call(tokens)
|
||||||
} else if tokens[0] == Token::Keyword("let".to_owned()) {
|
} else if tokens[0] == Token::Keyword("let".to_owned()) {
|
||||||
if tokens.len() < 2 {
|
if tokens.len() < 2 {
|
||||||
Err(InterpreterError::UnexpectedEOF)
|
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> {
|
fn parse_unary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
if tokens[index] != Token::Minus && tokens[index] != Token::Bang {
|
if tokens[index] != Token::Minus && tokens[index] != Token::Bang {
|
||||||
return parse_grouped_expression(&tokens[index..]);
|
parse_grouped_expression(&tokens[index..])
|
||||||
} else {
|
} else {
|
||||||
let operation = tokens[index].clone();
|
let operation = tokens[index].clone();
|
||||||
index += 1;
|
index += 1;
|
||||||
@ -396,180 +283,9 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
|
|||||||
if index >= tokens.len() {
|
if index >= tokens.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let statement = parse_expression(&tokens[index..]);
|
let (statement, tokens_consumed) = parse_expression(&tokens[index..])?;
|
||||||
if let Ok((statement, tokens_consumed)) = statement {
|
statements.push(statement);
|
||||||
statements.push(statement);
|
index += tokens_consumed;
|
||||||
index += tokens_consumed;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(AstNode::Program { statements })
|
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