Make basic stuff not suck
This commit is contained in:
parent
257c948c3b
commit
643b4c8538
57
Cargo.lock
generated
57
Cargo.lock
generated
@ -1,13 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
version = 3
|
||||||
name = "aho-corasick"
|
|
||||||
version = "0.7.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
@ -21,34 +14,15 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128"
|
name = "leb128"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
|
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nyst"
|
name = "nyst"
|
||||||
version = "0.1.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e268c6d6d5d5da20b69ab42e6ca2900ae75104db56d9fc4922a831499925c246"
|
|
||||||
dependencies = [
|
|
||||||
"regex",
|
|
||||||
"ron",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pivot"
|
name = "pivot"
|
||||||
@ -78,24 +52,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
"thread_local",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.6.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@ -138,15 +94,6 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -11,4 +11,4 @@ repository = "https://github.com/garentyler/pivot"
|
|||||||
ron = "0.6.2"
|
ron = "0.6.2"
|
||||||
serde = "1.0.117"
|
serde = "1.0.117"
|
||||||
wat = "1.0.29"
|
wat = "1.0.29"
|
||||||
nyst = "*"
|
nyst = { path = "../nyst" }
|
||||||
|
328
src/ast.rs
328
src/ast.rs
@ -1,253 +1,195 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum AstNodeKind {
|
pub enum AstNode {
|
||||||
// Primitives
|
// Primitives
|
||||||
Integer,
|
Integer(i32),
|
||||||
Identifier,
|
Identifier(String),
|
||||||
|
String(String),
|
||||||
|
Boolean(bool),
|
||||||
// Unary operators
|
// Unary operators
|
||||||
Not,
|
Not {
|
||||||
|
operand: Box<AstNode>,
|
||||||
|
},
|
||||||
// Infix operators
|
// Infix operators
|
||||||
NotEqual,
|
Equal {
|
||||||
Equal,
|
left: Box<AstNode>,
|
||||||
Add,
|
right: Box<AstNode>,
|
||||||
Subtract,
|
},
|
||||||
Multiply,
|
Add {
|
||||||
Divide,
|
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
|
// Control flow
|
||||||
Block,
|
Block {
|
||||||
IfStatement,
|
statements: Vec<AstNode>,
|
||||||
WhileLoop,
|
},
|
||||||
Program,
|
If {
|
||||||
|
condition: Box<AstNode>,
|
||||||
|
consequence: Box<AstNode>,
|
||||||
|
alternative: Option<Box<AstNode>>,
|
||||||
|
},
|
||||||
|
While {
|
||||||
|
condition: Box<AstNode>,
|
||||||
|
body: Box<AstNode>,
|
||||||
|
},
|
||||||
// Functions and variables
|
// Functions and variables
|
||||||
FunctionCall,
|
FunctionCall {
|
||||||
FunctionReturn,
|
identifier: Box<AstNode>,
|
||||||
FunctionDefinition,
|
arguments: Vec<AstNode>,
|
||||||
VariableDefinition,
|
},
|
||||||
VariableDeclaration,
|
FunctionReturn {
|
||||||
Assign,
|
value: Box<AstNode>,
|
||||||
// Import
|
},
|
||||||
Import,
|
FunctionDefinition {
|
||||||
// Blank node
|
identifier: Box<AstNode>,
|
||||||
|
arguments: Vec<AstNode>,
|
||||||
|
body: Box<AstNode>,
|
||||||
|
},
|
||||||
|
VariableDeclaration {
|
||||||
|
identifier: Box<AstNode>,
|
||||||
|
},
|
||||||
|
// Other
|
||||||
|
Import {
|
||||||
|
identifier: Box<AstNode>,
|
||||||
|
},
|
||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct AstNode {
|
|
||||||
pub kind: AstNodeKind,
|
|
||||||
pub value: String,
|
|
||||||
pub subnodes: Vec<AstNode>,
|
|
||||||
}
|
|
||||||
impl AstNode {
|
impl AstNode {
|
||||||
pub fn new(kind: AstNodeKind, value: String, subnodes: Vec<AstNode>) -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind,
|
|
||||||
value,
|
|
||||||
subnodes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitives
|
// Primitives
|
||||||
pub fn integer(num: i64) -> AstNode {
|
pub fn integer(value: i32) -> AstNode {
|
||||||
AstNode {
|
AstNode::Integer(value)
|
||||||
kind: AstNodeKind::Integer,
|
|
||||||
value: num.to_string(),
|
|
||||||
subnodes: vec![],
|
|
||||||
}
|
}
|
||||||
|
pub fn identifier(value: String) -> AstNode {
|
||||||
|
AstNode::Identifier(value)
|
||||||
}
|
}
|
||||||
pub fn identifier(id: String) -> AstNode {
|
pub fn string(value: String) -> AstNode {
|
||||||
AstNode {
|
AstNode::String(value)
|
||||||
kind: AstNodeKind::Identifier,
|
|
||||||
value: id,
|
|
||||||
subnodes: vec![],
|
|
||||||
}
|
}
|
||||||
|
pub fn boolean(value: bool) -> AstNode {
|
||||||
|
AstNode::Boolean(value)
|
||||||
}
|
}
|
||||||
// Unary operators
|
// Unary operators
|
||||||
pub fn not(operand: AstNode) -> AstNode {
|
pub fn not(operand: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Not {
|
||||||
kind: AstNodeKind::Not,
|
operand: Box::new(operand),
|
||||||
value: "not".into(),
|
|
||||||
subnodes: vec![operand],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Infix operators
|
// Infix operators
|
||||||
pub fn not_equal(left: AstNode, right: AstNode) -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::NotEqual,
|
|
||||||
value: "not_equal".into(),
|
|
||||||
subnodes: vec![left, right],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn equal(left: AstNode, right: AstNode) -> AstNode {
|
pub fn equal(left: AstNode, right: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Equal {
|
||||||
kind: AstNodeKind::Equal,
|
left: Box::new(left),
|
||||||
value: "equal".into(),
|
right: Box::new(right),
|
||||||
subnodes: vec![left, 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 {
|
pub fn add(left: AstNode, right: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Add {
|
||||||
kind: AstNodeKind::Add,
|
left: Box::new(left),
|
||||||
value: "add".into(),
|
right: Box::new(right),
|
||||||
subnodes: vec![left, right],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn subtract(left: AstNode, right: AstNode) -> AstNode {
|
pub fn subtract(left: AstNode, right: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Subtract {
|
||||||
kind: AstNodeKind::Subtract,
|
left: Box::new(left),
|
||||||
value: "subtract".into(),
|
right: Box::new(right),
|
||||||
subnodes: vec![left, right],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn multiply(left: AstNode, right: AstNode) -> AstNode {
|
pub fn multiply(left: AstNode, right: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Multiply {
|
||||||
kind: AstNodeKind::Multiply,
|
left: Box::new(left),
|
||||||
value: "multiply".into(),
|
right: Box::new(right),
|
||||||
subnodes: vec![left, right],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn divide(left: AstNode, right: AstNode) -> AstNode {
|
pub fn divide(left: AstNode, right: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::Divide {
|
||||||
kind: AstNodeKind::Divide,
|
left: Box::new(left),
|
||||||
value: "divide".into(),
|
right: Box::new(right),
|
||||||
subnodes: vec![left, right],
|
}
|
||||||
|
}
|
||||||
|
pub fn assign(left: AstNode, right: AstNode) -> AstNode {
|
||||||
|
AstNode::Assign {
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Control flow
|
// Control flow
|
||||||
pub fn block(statements: Vec<AstNode>) -> AstNode {
|
pub fn block(statements: Vec<AstNode>) -> AstNode {
|
||||||
AstNode {
|
AstNode::Block { statements }
|
||||||
kind: AstNodeKind::Block,
|
|
||||||
value: "block".into(),
|
|
||||||
subnodes: statements,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn if_statement(
|
pub fn if_statement(
|
||||||
conditional: AstNode,
|
condition: AstNode,
|
||||||
consequence: AstNode,
|
consequence: AstNode,
|
||||||
alternative: AstNode,
|
alternative: Option<AstNode>,
|
||||||
) -> AstNode {
|
) -> AstNode {
|
||||||
AstNode {
|
AstNode::If {
|
||||||
kind: AstNodeKind::IfStatement,
|
condition: Box::new(condition),
|
||||||
value: "if_statement".into(),
|
consequence: Box::new(consequence),
|
||||||
subnodes: vec![conditional, consequence, alternative],
|
alternative: alternative.and_then(|alt| Some(Box::new(alt))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn while_loop(conditional: AstNode, body: AstNode) -> AstNode {
|
pub fn while_loop(condition: AstNode, body: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::While {
|
||||||
kind: AstNodeKind::WhileLoop,
|
condition: Box::new(condition),
|
||||||
value: "while_loop".into(),
|
body: Box::new(body),
|
||||||
subnodes: vec![conditional, body],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn program(statements: Vec<AstNode>) -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::Program,
|
|
||||||
value: "program".into(),
|
|
||||||
subnodes: statements,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Functions and variables
|
// Functions and variables
|
||||||
pub fn function_call(name: String, parameters: Vec<AstNode>) -> AstNode {
|
pub fn function_call(identifier: AstNode, arguments: Vec<AstNode>) -> AstNode {
|
||||||
AstNode {
|
AstNode::FunctionCall {
|
||||||
kind: AstNodeKind::FunctionCall,
|
identifier: Box::new(identifier),
|
||||||
value: name,
|
arguments,
|
||||||
subnodes: parameters,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn function_return(operand: AstNode) -> AstNode {
|
pub fn function_return(value: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::FunctionReturn {
|
||||||
kind: AstNodeKind::FunctionReturn,
|
value: Box::new(value),
|
||||||
value: "return".into(),
|
|
||||||
subnodes: vec![operand],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn function_definition(name: String, parameters: Vec<AstNode>, body: AstNode) -> AstNode {
|
pub fn function_definition(
|
||||||
let mut params = vec![body];
|
identifier: AstNode,
|
||||||
for p in parameters {
|
arguments: Vec<AstNode>,
|
||||||
params.push(p);
|
body: AstNode,
|
||||||
}
|
) -> AstNode {
|
||||||
AstNode {
|
AstNode::FunctionDefinition {
|
||||||
kind: AstNodeKind::FunctionDefinition,
|
identifier: Box::new(identifier),
|
||||||
value: name,
|
arguments,
|
||||||
subnodes: params,
|
body: Box::new(body),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn variable_definition(name: String, value: AstNode) -> AstNode {
|
pub fn variable_declaration(identifier: AstNode) -> AstNode {
|
||||||
AstNode {
|
AstNode::VariableDeclaration {
|
||||||
kind: AstNodeKind::VariableDefinition,
|
identifier: Box::new(identifier),
|
||||||
value: name,
|
|
||||||
subnodes: vec![value],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn variable_declaration(name: String) -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::VariableDeclaration,
|
|
||||||
value: name,
|
|
||||||
subnodes: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn assign(name: String, value: AstNode) -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::Assign,
|
|
||||||
value: name,
|
|
||||||
subnodes: vec![value],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Import
|
|
||||||
pub fn import(num_args: AstNode, returns_value: AstNode, mut fn_path: Vec<AstNode>) -> AstNode {
|
|
||||||
let mut data = vec![num_args, returns_value];
|
|
||||||
data.append(&mut fn_path);
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::Import,
|
|
||||||
value: "import".into(),
|
|
||||||
subnodes: data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Blank node
|
|
||||||
pub fn null() -> AstNode {
|
|
||||||
AstNode {
|
|
||||||
kind: AstNodeKind::Null,
|
|
||||||
value: "".into(),
|
|
||||||
subnodes: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
pub fn pretty_print(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result {
|
pub fn import(identifier: AstNode) -> AstNode {
|
||||||
for _ in 0..indent {
|
AstNode::Import {
|
||||||
write!(f, " ")?;
|
identifier: Box::new(identifier),
|
||||||
}
|
}
|
||||||
write!(f, "{{\n")?;
|
|
||||||
for _ in 0..indent + 2 {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
}
|
||||||
write!(f, "kind: {:?}\n", self.kind)?;
|
pub fn null() -> AstNode {
|
||||||
for _ in 0..indent + 2 {
|
AstNode::Null
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
write!(f, "value: {:?}\n", self.value)?;
|
|
||||||
if self.subnodes.len() > 0 {
|
|
||||||
for _ in 0..indent + 2 {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
write!(f, "subnodes: [\n")?;
|
|
||||||
for subnode in &self.subnodes {
|
|
||||||
subnode.pretty_print(f, indent + 4)?;
|
|
||||||
write!(f, ",\n")?;
|
|
||||||
}
|
|
||||||
for _ in 0..indent + 2 {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
write!(f, "]\n")?;
|
|
||||||
}
|
|
||||||
for _ in 0..indent {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for AstNode {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.pretty_print(f, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
23
src/lib.rs
23
src/lib.rs
@ -1,18 +1,19 @@
|
|||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod codegen;
|
// pub mod codegen;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
||||||
use codegen::{SymbolGenerator, Wasm};
|
// use codegen::{SymbolGenerator, Wasm};
|
||||||
|
|
||||||
pub fn compile<T: Into<String>>(src: T) -> Vec<u8> {
|
pub fn compile(source: &str) -> Vec<u8> {
|
||||||
wat::parse_str(compile_wat(src)).unwrap()
|
wat::parse_str(compile_wat(source)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_wat<T: Into<String>>(src: T) -> String {
|
pub fn compile_wat(source: &str) -> String {
|
||||||
let mut s = SymbolGenerator::new();
|
// let mut s = SymbolGenerator::new();
|
||||||
let ast = parse::parse(src);
|
let ast = parse::interpret(source);
|
||||||
println!("{}", ast);
|
// println!("{:?}", ast);
|
||||||
let wasm = ast.emit(&mut s);
|
unimplemented!()
|
||||||
println!("{}", wasm);
|
// let wasm = ast.emit(&mut s);
|
||||||
wasm
|
// println!("{}", wasm);
|
||||||
|
// wasm
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ 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 mut src = String::new();
|
let source = std::fs::read_to_string("test.pvt").unwrap();
|
||||||
File::open("test.pvt")?.read_to_string(&mut src)?;
|
|
||||||
// Compile it
|
// Compile it
|
||||||
let binary = pivot::compile(src);
|
let _value = pivot::parse::interpret(&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.wasm")?.write_all(&binary)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
760
src/parse.rs
760
src/parse.rs
@ -1,401 +1,381 @@
|
|||||||
use crate::ast::{AstNode, AstNodeKind};
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
use nyst::Parser;
|
pub enum Token {
|
||||||
use ron::{from_str, to_string};
|
Integer(i64),
|
||||||
|
Float(f64),
|
||||||
pub fn parse<T: Into<String>>(src: T) -> AstNode {
|
String(String),
|
||||||
let src: String = src.into();
|
Boolean(bool),
|
||||||
let whitespace = Parser::regex(r"[ \n\r\t]+");
|
Plus,
|
||||||
let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]"));
|
Minus,
|
||||||
let ignored = whitespace.or(comments).repeat_range(0..usize::MAX);
|
Star,
|
||||||
let statement = ignored
|
Slash,
|
||||||
.optional()
|
Bang,
|
||||||
.ignore()
|
Quote(char),
|
||||||
.and(Parser::custom(parse_statement));
|
Parenthesis { closing: bool },
|
||||||
let parse_program = statement
|
Whitespace(String),
|
||||||
.clone()
|
Unknown,
|
||||||
.repeat_range(0..usize::MAX)
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let mut statements = vec![];
|
|
||||||
for d in data {
|
|
||||||
statements.push(from_str::<AstNode>(&d)?);
|
|
||||||
}
|
|
||||||
Ok(to_string(&AstNode::program(statements))?)
|
|
||||||
});
|
|
||||||
from_str::<AstNode>(&parse_program.parse(src).unwrap().0).unwrap()
|
|
||||||
}
|
}
|
||||||
fn parse_statement(src: String) -> Result<(String, String), String> {
|
|
||||||
let whitespace = Parser::regex(r"[ \n\r\t]+");
|
#[derive(Debug)]
|
||||||
let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]"));
|
pub enum InterpreterError {
|
||||||
let ignored = whitespace.or(comments).repeat_range(0..usize::MAX);
|
/// Error parsing source
|
||||||
// Token parser constructor.
|
ParseError(String),
|
||||||
let i = ignored.clone();
|
/// Unexpected EOF
|
||||||
let token = move |pattern: &str| i.clone().ignore().and(Parser::regex(pattern));
|
UnexpectedEOF,
|
||||||
// Token helper parsers.
|
}
|
||||||
let function = Parser::regex(r"function\b").or(ignored.clone());
|
|
||||||
let return_token = token(r"return\b");
|
pub fn tokenize(source: &str) -> Result<Vec<Token>, InterpreterError> {
|
||||||
let semicolon = token(r"[;]");
|
fn tokenize_number(chars: &[char]) -> Result<(Token, usize), ()> {
|
||||||
let if_token = token(r"if\b");
|
let mut current_value = String::new();
|
||||||
let else_token = token(r"else\b");
|
let mut chars_consumed = 0;
|
||||||
let left_paren = token(r"[(]");
|
for c in chars {
|
||||||
let right_paren = token(r"[)]");
|
if !c.is_digit(10) && *c != '.' {
|
||||||
let left_brace = token(r"[{]");
|
break;
|
||||||
let right_brace = token(r"[}]");
|
|
||||||
let while_token = token(r"while\b");
|
|
||||||
let var = token(r"var\b");
|
|
||||||
let number = token(r"[0-9]+").map(|matched| {
|
|
||||||
Ok(to_string(&AstNode::integer(
|
|
||||||
matched.parse::<i64>().unwrap(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let identifier = token(r"[a-zA-Z_][a-zA-Z0-9_]*")
|
|
||||||
.map(|matched| Ok(to_string(&AstNode::identifier(matched))?));
|
|
||||||
let assign =
|
|
||||||
token(r"=").map(|_matched| Ok(to_string(&AstNode::assign("".into(), AstNode::null()))?));
|
|
||||||
let comma = token(r"[,]");
|
|
||||||
let period = token(r"[.]");
|
|
||||||
let expression = Parser::custom(parse_expression);
|
|
||||||
let statement = Parser::custom(parse_statement);
|
|
||||||
let import = token(r"import\b");
|
|
||||||
// Statement parsers.
|
|
||||||
let import_statement = import
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(identifier.clone())
|
|
||||||
.and(
|
|
||||||
period
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(identifier.clone())
|
|
||||||
.repeat_range(0..usize::MAX),
|
|
||||||
)
|
|
||||||
.and(number.clone())
|
|
||||||
.and(number.clone())
|
|
||||||
.and(semicolon.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let import_fn_returns_value = from_str::<AstNode>(&data[1])?;
|
|
||||||
let data = from_str::<Vec<String>>(&data[0])?;
|
|
||||||
let num_import_fn_args = from_str::<AstNode>(&data[1])?;
|
|
||||||
let data = from_str::<Vec<String>>(&data[0])?;
|
|
||||||
let mut import_fn_path = vec![];
|
|
||||||
import_fn_path.push(from_str::<AstNode>(&data[0])?);
|
|
||||||
let data = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
for d in data {
|
|
||||||
import_fn_path.push(from_str::<AstNode>(&d)?);
|
|
||||||
}
|
}
|
||||||
Ok(to_string(&AstNode::import(
|
current_value.push(*c);
|
||||||
num_import_fn_args,
|
chars_consumed += 1;
|
||||||
import_fn_returns_value,
|
|
||||||
import_fn_path,
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let return_statement = return_token
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(semicolon.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<AstNode>(&matched)?;
|
|
||||||
Ok(to_string(&AstNode::function_return(data))?)
|
|
||||||
});
|
|
||||||
let expression_statement = expression.clone().and(semicolon.clone().ignore());
|
|
||||||
let if_statement = if_token
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(
|
|
||||||
left_paren
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(right_paren.clone().ignore()),
|
|
||||||
)
|
|
||||||
.and(statement.clone())
|
|
||||||
.and(
|
|
||||||
else_token
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(statement.clone())
|
|
||||||
.optional(),
|
|
||||||
)
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let alternative = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
let alternative = match alternative.get(0) {
|
|
||||||
Some(s) => from_str::<AstNode>(&s)?,
|
|
||||||
None => AstNode::null(),
|
|
||||||
};
|
|
||||||
let others = from_str::<Vec<String>>(&data[0])?;
|
|
||||||
let conditional = from_str::<AstNode>(&others[0])?;
|
|
||||||
let consequence = from_str::<AstNode>(&others[1])?;
|
|
||||||
Ok(to_string(&AstNode::if_statement(
|
|
||||||
conditional,
|
|
||||||
consequence,
|
|
||||||
alternative,
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let while_statement = while_token
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(
|
|
||||||
left_paren
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(right_paren.clone().ignore()),
|
|
||||||
)
|
|
||||||
.and(statement.clone())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let conditional = from_str::<AstNode>(&data[0])?;
|
|
||||||
let body = from_str::<AstNode>(&data[1])?;
|
|
||||||
Ok(to_string(&AstNode::while_loop(conditional, body))?)
|
|
||||||
});
|
|
||||||
let var_statement = var
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(identifier.clone())
|
|
||||||
.and(assign.clone().ignore())
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(semicolon.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let name = from_str::<AstNode>(&data[0])?.value;
|
|
||||||
let value = from_str::<AstNode>(&data[1])?;
|
|
||||||
Ok(to_string(&AstNode::variable_definition(name, value))?)
|
|
||||||
});
|
|
||||||
let assignment_statement = identifier
|
|
||||||
.clone()
|
|
||||||
.and(assign.clone().ignore())
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(semicolon.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let name = from_str::<AstNode>(&data[0])?.value;
|
|
||||||
let value = from_str::<AstNode>(&data[1])?;
|
|
||||||
Ok(to_string(&AstNode::assign(name, value))?)
|
|
||||||
});
|
|
||||||
let block_statement = left_brace
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(statement.clone().repeat_range(0..usize::MAX))
|
|
||||||
.and(right_brace.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let mut statements = vec![];
|
|
||||||
for d in data {
|
|
||||||
statements.push(from_str::<AstNode>(&d)?);
|
|
||||||
}
|
}
|
||||||
Ok(to_string(&AstNode::block(statements))?)
|
if chars_consumed == 0 {
|
||||||
});
|
Err(())
|
||||||
let args = identifier
|
} else if current_value.contains(".") {
|
||||||
.clone()
|
Ok((Token::Float(current_value.parse::<f64>().unwrap()), chars_consumed))
|
||||||
.and(
|
} else {
|
||||||
comma
|
Ok((Token::Integer(current_value.parse::<i64>().unwrap()), chars_consumed))
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(identifier.clone())
|
|
||||||
.repeat_range(0..usize::MAX),
|
|
||||||
)
|
|
||||||
.map(|matched| {
|
|
||||||
let mut args = vec![];
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
args.push(data[0].clone());
|
|
||||||
let others = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
for o in others {
|
|
||||||
args.push(o.clone());
|
|
||||||
}
|
|
||||||
Ok(to_string(&args)?)
|
|
||||||
});
|
|
||||||
let function_statement = function
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(identifier.clone())
|
|
||||||
.and(
|
|
||||||
left_paren
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(args.clone().optional())
|
|
||||||
.and(right_paren.clone().ignore()),
|
|
||||||
)
|
|
||||||
.and(block_statement.clone())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let mut body = from_str::<AstNode>(&data[1])?;
|
|
||||||
let data = from_str::<Vec<String>>(&data[0])?;
|
|
||||||
let name = from_str::<AstNode>(&data[0])?.value;
|
|
||||||
let params = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
let mut parameters = vec![];
|
|
||||||
if params.len() != 0 {
|
|
||||||
for p in from_str::<Vec<String>>(¶ms[0])? {
|
|
||||||
parameters.push(from_str::<AstNode>(&p)?);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hoist variable definitions.
|
fn tokenize_bool(chars: &[char]) -> Result<(Token, usize), ()> {
|
||||||
let mut vars = vec![];
|
if chars.len() >= 5 && chars[0..5] == ['f', 'a', 'l', 's', 'e'] {
|
||||||
let mut others = vec![];
|
Ok((Token::Boolean(false), 5))
|
||||||
for node in &body.subnodes {
|
} else if chars.len() >= 4 && chars[0..4] == ['t', 'r', 'u', 'e'] {
|
||||||
match node.kind {
|
Ok((Token::Boolean(true), 4))
|
||||||
AstNodeKind::VariableDefinition => {
|
} else {
|
||||||
vars.push(AstNode::variable_declaration(node.value.clone()));
|
Err(())
|
||||||
others.push(AstNode::assign(
|
}
|
||||||
node.value.clone(),
|
}
|
||||||
node.subnodes[0].clone(),
|
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 {
|
||||||
|
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_bool(&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::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)]
|
||||||
|
pub enum AstPrimitive {
|
||||||
|
Integer(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
Boolean(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub enum AstNode {
|
||||||
|
Primitive(AstPrimitive),
|
||||||
|
Negate { body: 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> },
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
|
||||||
|
fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
Err(InterpreterError::UnexpectedEOF)
|
||||||
|
} else if let Token::Integer(n) = &tokens[0] {
|
||||||
|
Ok((AstNode::Primitive(AstPrimitive::Integer(*n)), 1))
|
||||||
|
} else if let Token::Float(n) = &tokens[0] {
|
||||||
|
Ok((AstNode::Primitive(AstPrimitive::Float(*n)), 1))
|
||||||
|
} else if let Token::Boolean(n) = &tokens[0] {
|
||||||
|
Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1))
|
||||||
|
} else if let Token::String(s) = &tokens[0] {
|
||||||
|
Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1))
|
||||||
|
} else {
|
||||||
|
Err(InterpreterError::ParseError("Expected literal".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_grouped_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
|
let mut index = 0;
|
||||||
|
// '('
|
||||||
|
if !matches!(tokens[index], Token::Parenthesis { closing: false }) {
|
||||||
|
return parse_primary_expression(tokens);
|
||||||
|
} else {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
// expression of any kind
|
||||||
|
let (value, tokens_consumed) = parse_expression(&tokens[index..])?;
|
||||||
|
index += tokens_consumed;
|
||||||
|
// ')'
|
||||||
|
if !matches!(tokens[index], Token::Parenthesis { closing: true }) {
|
||||||
|
return Err(InterpreterError::ParseError("No closing parenthesis".to_owned()));
|
||||||
|
} else {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
Ok((value, index))
|
||||||
|
}
|
||||||
|
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..]);
|
||||||
|
} else {
|
||||||
|
let operation = tokens[index].clone();
|
||||||
|
index += 1;
|
||||||
|
let (body, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
|
||||||
|
index += tokens_consumed;
|
||||||
|
Ok((
|
||||||
|
match operation {
|
||||||
|
Token::Minus => AstNode::Negate { body: Box::new(body) },
|
||||||
|
Token::Bang => AstNode::Negate { body: Box::new(body) },
|
||||||
|
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())),
|
||||||
|
},
|
||||||
|
index
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => others.push(node.clone()),
|
|
||||||
}
|
}
|
||||||
|
fn parse_multiplication_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
|
let mut index = 0;
|
||||||
|
let (mut value, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
|
||||||
|
index += tokens_consumed;
|
||||||
|
while index < tokens.len() {
|
||||||
|
if tokens[index] != Token::Star && tokens[index] != Token::Slash {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
vars.append(&mut others);
|
let operation = tokens[index].clone();
|
||||||
body.subnodes = vars;
|
index += 1;
|
||||||
Ok(to_string(&AstNode::function_definition(
|
let (right, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
|
||||||
name, parameters, body,
|
index += tokens_consumed;
|
||||||
))?)
|
value = match operation {
|
||||||
});
|
Token::Star => AstNode::Multiply {
|
||||||
return_statement
|
left: Box::new(value),
|
||||||
.clone()
|
right: Box::new(right),
|
||||||
.or(import_statement.clone())
|
},
|
||||||
.or(if_statement.clone())
|
Token::Slash => AstNode::Divide {
|
||||||
.or(while_statement.clone())
|
left: Box::new(value),
|
||||||
.or(var_statement.clone())
|
right: Box::new(right),
|
||||||
.or(assignment_statement.clone())
|
},
|
||||||
.or(block_statement.clone())
|
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())),
|
||||||
.or(function_statement.clone())
|
|
||||||
.or(expression_statement.clone())
|
|
||||||
.parse(src)
|
|
||||||
}
|
|
||||||
fn parse_expression(src: String) -> Result<(String, String), String> {
|
|
||||||
let whitespace = Parser::regex(r"[ \n\r\t]+");
|
|
||||||
let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]"));
|
|
||||||
let ignored = whitespace.or(comments).repeat_range(0..usize::MAX);
|
|
||||||
// Token parser constructor.
|
|
||||||
let i = ignored.clone();
|
|
||||||
let token = move |pattern: &str| i.clone().ignore().and(Parser::regex(pattern));
|
|
||||||
// Token helper parsers.
|
|
||||||
let comma = token(r"[,]");
|
|
||||||
let left_paren = token(r"[(]");
|
|
||||||
let right_paren = token(r"[)]");
|
|
||||||
let number = token(r"[0-9]+").map(|matched| {
|
|
||||||
Ok(to_string(&AstNode::integer(
|
|
||||||
matched.parse::<i64>().unwrap(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let identifier = token(r"[a-zA-Z_][a-zA-Z0-9_]*")
|
|
||||||
.map(|matched| Ok(to_string(&AstNode::identifier(matched))?));
|
|
||||||
let not = token(r"!").map(|_matched| Ok(to_string(&AstNode::not(AstNode::null()))?));
|
|
||||||
let equal = token(r"==").map(|_matched| {
|
|
||||||
Ok(to_string(&AstNode::equal(
|
|
||||||
AstNode::null(),
|
|
||||||
AstNode::null(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let not_equal = token(r"!=").map(|_matched| {
|
|
||||||
Ok(to_string(&AstNode::not_equal(
|
|
||||||
AstNode::null(),
|
|
||||||
AstNode::null(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let plus = token(r"[+]")
|
|
||||||
.map(|_matched| Ok(to_string(&AstNode::add(AstNode::null(), AstNode::null()))?));
|
|
||||||
let minus = token(r"[-]").map(|_matched| {
|
|
||||||
Ok(to_string(&AstNode::subtract(
|
|
||||||
AstNode::null(),
|
|
||||||
AstNode::null(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let star = token(r"[*]").map(|_matched| {
|
|
||||||
Ok(to_string(&AstNode::multiply(
|
|
||||||
AstNode::null(),
|
|
||||||
AstNode::null(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
let slash = token(r"[/]").map(|_matched| {
|
|
||||||
Ok(to_string(&AstNode::divide(
|
|
||||||
AstNode::null(),
|
|
||||||
AstNode::null(),
|
|
||||||
))?)
|
|
||||||
});
|
|
||||||
// Expression parsers.
|
|
||||||
let expression = Parser::custom(parse_expression);
|
|
||||||
let args = expression
|
|
||||||
.clone()
|
|
||||||
.and(
|
|
||||||
comma
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(expression.clone())
|
|
||||||
.repeat_range(0..usize::MAX),
|
|
||||||
)
|
|
||||||
.map(|matched| {
|
|
||||||
let mut args = vec![];
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
args.push(data[0].clone());
|
|
||||||
let others = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
for o in others {
|
|
||||||
args.push(o.clone());
|
|
||||||
}
|
|
||||||
Ok(to_string(&args)?)
|
|
||||||
});
|
|
||||||
let call = identifier
|
|
||||||
.clone()
|
|
||||||
.and(left_paren.clone().ignore())
|
|
||||||
.and(args.clone().optional())
|
|
||||||
.and(right_paren.clone().ignore())
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let callee = data[0].clone();
|
|
||||||
let args = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
let mut ast_args = vec![];
|
|
||||||
if let Some(args) = args.get(0) {
|
|
||||||
let args = from_str::<Vec<String>>(&args)?;
|
|
||||||
for arg in &args {
|
|
||||||
ast_args.push(from_str::<AstNode>(arg)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(to_string(&AstNode::function_call(callee, ast_args))?)
|
|
||||||
});
|
|
||||||
let atom = call
|
|
||||||
.clone()
|
|
||||||
.or(identifier.clone())
|
|
||||||
.or(number.clone())
|
|
||||||
.or(left_paren
|
|
||||||
.clone()
|
|
||||||
.ignore()
|
|
||||||
.and(expression.clone())
|
|
||||||
.and(right_paren.clone().ignore()));
|
|
||||||
let unary = not.clone().optional().and(atom.clone()).map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let atom_data = from_str::<AstNode>(&data[1])?;
|
|
||||||
Ok(to_string(&match &data[0][..] {
|
|
||||||
"!" => AstNode::not(atom_data),
|
|
||||||
_ => atom_data,
|
|
||||||
})?)
|
|
||||||
});
|
|
||||||
let infix = |operator_parser: Parser, term_parser: Parser| {
|
|
||||||
term_parser
|
|
||||||
.clone()
|
|
||||||
.and(
|
|
||||||
operator_parser
|
|
||||||
.and(term_parser.clone())
|
|
||||||
.repeat_range(0..usize::MAX),
|
|
||||||
)
|
|
||||||
.map(|matched| {
|
|
||||||
let data = from_str::<Vec<String>>(&matched)?;
|
|
||||||
let others = from_str::<Vec<String>>(&data[1])?;
|
|
||||||
let mut current = from_str::<AstNode>(&data[0])?;
|
|
||||||
for i in 0..others.len() {
|
|
||||||
let o = from_str::<Vec<String>>(&others[i])?; // Parse the [operator, unary]
|
|
||||||
let mut op = from_str::<AstNode>(&o[0])?; // Pull the operator out.
|
|
||||||
let t = from_str::<AstNode>(&o[1])?; // Pull the term out.
|
|
||||||
op.subnodes[0] = current; // Put current on the left side.
|
|
||||||
op.subnodes[1] = t; // Put the term on the right side.
|
|
||||||
current = op; // Replace current with the operator.
|
|
||||||
}
|
|
||||||
Ok(to_string(¤t)?)
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
let product = infix(star.clone().or(slash.clone()), unary.clone());
|
}
|
||||||
let sum = infix(plus.clone().or(minus.clone()), product.clone());
|
Ok((value, index))
|
||||||
let comparison = infix(equal.clone().or(not_equal.clone()), sum.clone());
|
}
|
||||||
comparison.parse(src)
|
fn parse_addition_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
|
let mut index = 0;
|
||||||
|
let (mut value, tokens_consumed) = parse_multiplication_expression(&tokens[index..])?;
|
||||||
|
index += tokens_consumed;
|
||||||
|
while index < tokens.len() {
|
||||||
|
if tokens[index] != Token::Plus && tokens[index] != Token::Minus {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let operation = tokens[index].clone();
|
||||||
|
index += 1;
|
||||||
|
let (right, tokens_consumed) = parse_multiplication_expression(&tokens[index..])?;
|
||||||
|
index += tokens_consumed;
|
||||||
|
value = match operation {
|
||||||
|
Token::Plus => AstNode::Add {
|
||||||
|
left: Box::new(value),
|
||||||
|
right: Box::new(right),
|
||||||
|
},
|
||||||
|
Token::Minus => AstNode::Subtract {
|
||||||
|
left: Box::new(value),
|
||||||
|
right: Box::new(right),
|
||||||
|
},
|
||||||
|
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok((value, index))
|
||||||
|
}
|
||||||
|
fn parse_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||||
|
parse_addition_expression(tokens)
|
||||||
|
}
|
||||||
|
let (ast, _) = parse_expression(tokens)?;
|
||||||
|
Ok(ast)
|
||||||
|
}
|
||||||
|
pub fn evaluate(ast: &AstNode) -> Result<AstPrimitive, InterpreterError> {
|
||||||
|
use std::mem::discriminant;
|
||||||
|
use AstNode::*;
|
||||||
|
match ast {
|
||||||
|
Primitive(p) => Ok(p.clone()),
|
||||||
|
Negate { body } => {
|
||||||
|
if let AstPrimitive::Integer(body) = evaluate(body)? {
|
||||||
|
Ok(AstPrimitive::Integer(body * -1))
|
||||||
|
} else if let AstPrimitive::Boolean(body) = evaluate(body)? {
|
||||||
|
Ok(AstPrimitive::Boolean(!body))
|
||||||
|
}else {
|
||||||
|
Err(InterpreterError::ParseError("Can only negate integers and bools".to_owned()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Add { left, right } => {
|
||||||
|
let left = evaluate(left)?;
|
||||||
|
let right = evaluate(right)?;
|
||||||
|
if discriminant(&left) != discriminant(&right) {
|
||||||
|
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
|
||||||
|
} else {
|
||||||
|
if let AstPrimitive::Integer(left) = left {
|
||||||
|
if let AstPrimitive::Integer(right) = right {
|
||||||
|
return Ok(AstPrimitive::Integer(left + right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let AstPrimitive::Float(left) = left {
|
||||||
|
if let AstPrimitive::Float(right) = right {
|
||||||
|
return Ok(AstPrimitive::Float(left + right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let AstPrimitive::String(left) = left {
|
||||||
|
if let AstPrimitive::String(right) = right {
|
||||||
|
return Ok(AstPrimitive::String(format!("{}{}", left, right)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(InterpreterError::ParseError("Can only add integers, strings, and floats".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subtract { left, right } => {
|
||||||
|
let left = evaluate(left)?;
|
||||||
|
let right = evaluate(right)?;
|
||||||
|
if discriminant(&left) != discriminant(&right) {
|
||||||
|
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
|
||||||
|
} else {
|
||||||
|
if let AstPrimitive::Integer(left) = left {
|
||||||
|
if let AstPrimitive::Integer(right) = right {
|
||||||
|
return Ok(AstPrimitive::Integer(left - right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let AstPrimitive::Float(left) = left {
|
||||||
|
if let AstPrimitive::Float(right) = right {
|
||||||
|
return Ok(AstPrimitive::Float(left - right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(InterpreterError::ParseError("Can only subtract integers and floats".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Multiply { left, right } => {
|
||||||
|
let left = evaluate(left)?;
|
||||||
|
let right = evaluate(right)?;
|
||||||
|
if discriminant(&left) != discriminant(&right) {
|
||||||
|
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
|
||||||
|
} else {
|
||||||
|
if let AstPrimitive::Integer(left) = left {
|
||||||
|
if let AstPrimitive::Integer(right) = right {
|
||||||
|
return Ok(AstPrimitive::Integer(left * right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let AstPrimitive::Float(left) = left {
|
||||||
|
if let AstPrimitive::Float(right) = right {
|
||||||
|
return Ok(AstPrimitive::Float(left * right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(InterpreterError::ParseError("Can only multiply integers and floats".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Divide { left, right } => {
|
||||||
|
let left = evaluate(left)?;
|
||||||
|
let right = evaluate(right)?;
|
||||||
|
if discriminant(&left) != discriminant(&right) {
|
||||||
|
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
|
||||||
|
} else {
|
||||||
|
if let AstPrimitive::Integer(left) = left {
|
||||||
|
if let AstPrimitive::Integer(right) = right {
|
||||||
|
return Ok(AstPrimitive::Integer(left / right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let AstPrimitive::Float(left) = left {
|
||||||
|
if let AstPrimitive::Float(right) = right {
|
||||||
|
return Ok(AstPrimitive::Float(left / right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(InterpreterError::ParseError("Can only divide integers and floats".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Null => Err(InterpreterError::ParseError("Cannot evaluate null".to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn interpret(source: &str) -> Result<(), InterpreterError> {
|
||||||
|
println!("source: {:?}", source);
|
||||||
|
let tokens = tokenize(source);
|
||||||
|
println!("tokens: {:?}", tokens);
|
||||||
|
let ast = parse(&tokens?);
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
let value = evaluate(&ast?);
|
||||||
|
println!("value: {:?}", value);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
13
test.txt
Normal file
13
test.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
PrimaryExpression = Identifier | Literal
|
||||||
|
GroupedExpression = PrimaryExpression | `(` Expression `)`
|
||||||
|
|
||||||
|
UnaryOperator = `-` | `!`
|
||||||
|
UnaryExpression = GroupedExpression | UnaryOperator UnaryExpression
|
||||||
|
|
||||||
|
MultiplyOperator = `*` | `/`
|
||||||
|
MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator UnaryExpression
|
||||||
|
|
||||||
|
AddOperator = `+` | `-`
|
||||||
|
AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression
|
||||||
|
|
||||||
|
Expression = AddExpression
|
Loading…
x
Reference in New Issue
Block a user