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.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@ -21,34 +14,15 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "nyst"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e268c6d6d5d5da20b69ab42e6ca2900ae75104db56d9fc4922a831499925c246"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"ron",
|
||||
"serde",
|
||||
]
|
||||
version = "0.5.2"
|
||||
|
||||
[[package]]
|
||||
name = "pivot"
|
||||
@ -78,24 +52,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "ron"
|
||||
version = "0.6.2"
|
||||
@ -138,15 +94,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
|
@ -11,4 +11,4 @@ repository = "https://github.com/garentyler/pivot"
|
||||
ron = "0.6.2"
|
||||
serde = "1.0.117"
|
||||
wat = "1.0.29"
|
||||
nyst = "*"
|
||||
nyst = { path = "../nyst" }
|
||||
|
334
src/ast.rs
334
src/ast.rs
@ -1,253 +1,195 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum AstNodeKind {
|
||||
pub enum AstNode {
|
||||
// Primitives
|
||||
Integer,
|
||||
Identifier,
|
||||
Integer(i32),
|
||||
Identifier(String),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
// Unary operators
|
||||
Not,
|
||||
Not {
|
||||
operand: Box<AstNode>,
|
||||
},
|
||||
// Infix operators
|
||||
NotEqual,
|
||||
Equal,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
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,
|
||||
IfStatement,
|
||||
WhileLoop,
|
||||
Program,
|
||||
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,
|
||||
FunctionReturn,
|
||||
FunctionDefinition,
|
||||
VariableDefinition,
|
||||
VariableDeclaration,
|
||||
Assign,
|
||||
// Import
|
||||
Import,
|
||||
// Blank node
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AstNode {
|
||||
pub kind: AstNodeKind,
|
||||
pub value: String,
|
||||
pub subnodes: Vec<AstNode>,
|
||||
}
|
||||
impl AstNode {
|
||||
pub fn new(kind: AstNodeKind, value: String, subnodes: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind,
|
||||
value,
|
||||
subnodes,
|
||||
}
|
||||
}
|
||||
|
||||
// Primitives
|
||||
pub fn integer(num: i64) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Integer,
|
||||
value: num.to_string(),
|
||||
subnodes: vec![],
|
||||
}
|
||||
pub fn integer(value: i32) -> AstNode {
|
||||
AstNode::Integer(value)
|
||||
}
|
||||
pub fn identifier(id: String) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Identifier,
|
||||
value: id,
|
||||
subnodes: vec![],
|
||||
}
|
||||
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 {
|
||||
kind: AstNodeKind::Not,
|
||||
value: "not".into(),
|
||||
subnodes: vec![operand],
|
||||
AstNode::Not {
|
||||
operand: Box::new(operand),
|
||||
}
|
||||
}
|
||||
// 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 {
|
||||
AstNode::Equal {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
pub fn equal(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Equal,
|
||||
value: "equal".into(),
|
||||
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 {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Add,
|
||||
value: "add".into(),
|
||||
subnodes: vec![left, right],
|
||||
AstNode::Add {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
pub fn subtract(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Subtract,
|
||||
value: "subtract".into(),
|
||||
subnodes: vec![left, right],
|
||||
AstNode::Subtract {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
pub fn multiply(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Multiply,
|
||||
value: "multiply".into(),
|
||||
subnodes: vec![left, right],
|
||||
AstNode::Multiply {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
pub fn divide(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Divide,
|
||||
value: "divide".into(),
|
||||
subnodes: vec![left, right],
|
||||
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 {
|
||||
kind: AstNodeKind::Block,
|
||||
value: "block".into(),
|
||||
subnodes: statements,
|
||||
}
|
||||
AstNode::Block { statements }
|
||||
}
|
||||
pub fn if_statement(
|
||||
conditional: AstNode,
|
||||
condition: AstNode,
|
||||
consequence: AstNode,
|
||||
alternative: AstNode,
|
||||
alternative: Option<AstNode>,
|
||||
) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::IfStatement,
|
||||
value: "if_statement".into(),
|
||||
subnodes: vec![conditional, consequence, alternative],
|
||||
AstNode::If {
|
||||
condition: Box::new(condition),
|
||||
consequence: Box::new(consequence),
|
||||
alternative: alternative.and_then(|alt| Some(Box::new(alt))),
|
||||
}
|
||||
}
|
||||
pub fn while_loop(conditional: AstNode, body: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::WhileLoop,
|
||||
value: "while_loop".into(),
|
||||
subnodes: vec![conditional, body],
|
||||
}
|
||||
}
|
||||
pub fn program(statements: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Program,
|
||||
value: "program".into(),
|
||||
subnodes: statements,
|
||||
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(name: String, parameters: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionCall,
|
||||
value: name,
|
||||
subnodes: parameters,
|
||||
pub fn function_call(identifier: AstNode, arguments: Vec<AstNode>) -> AstNode {
|
||||
AstNode::FunctionCall {
|
||||
identifier: Box::new(identifier),
|
||||
arguments,
|
||||
}
|
||||
}
|
||||
pub fn function_return(operand: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionReturn,
|
||||
value: "return".into(),
|
||||
subnodes: vec![operand],
|
||||
pub fn function_return(value: AstNode) -> AstNode {
|
||||
AstNode::FunctionReturn {
|
||||
value: Box::new(value),
|
||||
}
|
||||
}
|
||||
pub fn function_definition(name: String, parameters: Vec<AstNode>, body: AstNode) -> AstNode {
|
||||
let mut params = vec![body];
|
||||
for p in parameters {
|
||||
params.push(p);
|
||||
}
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionDefinition,
|
||||
value: name,
|
||||
subnodes: params,
|
||||
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_definition(name: String, value: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::VariableDefinition,
|
||||
value: name,
|
||||
subnodes: vec![value],
|
||||
pub fn variable_declaration(identifier: AstNode) -> AstNode {
|
||||
AstNode::VariableDeclaration {
|
||||
identifier: Box::new(identifier),
|
||||
}
|
||||
}
|
||||
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
|
||||
pub fn pretty_print(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result {
|
||||
for _ in 0..indent {
|
||||
write!(f, " ")?;
|
||||
pub fn import(identifier: AstNode) -> AstNode {
|
||||
AstNode::Import {
|
||||
identifier: Box::new(identifier),
|
||||
}
|
||||
write!(f, "{{\n")?;
|
||||
for _ in 0..indent + 2 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "kind: {:?}\n", self.kind)?;
|
||||
for _ in 0..indent + 2 {
|
||||
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)
|
||||
pub fn null() -> AstNode {
|
||||
AstNode::Null
|
||||
}
|
||||
}
|
||||
}
|
23
src/lib.rs
23
src/lib.rs
@ -1,18 +1,19 @@
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
// pub mod codegen;
|
||||
pub mod parse;
|
||||
|
||||
use codegen::{SymbolGenerator, Wasm};
|
||||
// use codegen::{SymbolGenerator, Wasm};
|
||||
|
||||
pub fn compile<T: Into<String>>(src: T) -> Vec<u8> {
|
||||
wat::parse_str(compile_wat(src)).unwrap()
|
||||
pub fn compile(source: &str) -> Vec<u8> {
|
||||
wat::parse_str(compile_wat(source)).unwrap()
|
||||
}
|
||||
|
||||
pub fn compile_wat<T: Into<String>>(src: T) -> String {
|
||||
let mut s = SymbolGenerator::new();
|
||||
let ast = parse::parse(src);
|
||||
println!("{}", ast);
|
||||
let wasm = ast.emit(&mut s);
|
||||
println!("{}", wasm);
|
||||
wasm
|
||||
pub fn compile_wat(source: &str) -> String {
|
||||
// let mut s = SymbolGenerator::new();
|
||||
let ast = parse::interpret(source);
|
||||
// println!("{:?}", ast);
|
||||
unimplemented!()
|
||||
// let wasm = ast.emit(&mut s);
|
||||
// println!("{}", wasm);
|
||||
// wasm
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ use std::io::prelude::*;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
// Read the source from a file.
|
||||
let mut src = String::new();
|
||||
File::open("test.pvt")?.read_to_string(&mut src)?;
|
||||
let source = std::fs::read_to_string("test.pvt").unwrap();
|
||||
// Compile it
|
||||
let binary = pivot::compile(src);
|
||||
let _value = pivot::parse::interpret(&source);
|
||||
// let binary = pivot::compile(&source);
|
||||
// Write it to a file.
|
||||
File::create("out.wasm")?.write_all(&binary)?;
|
||||
// File::create("out.wasm")?.write_all(&binary)?;
|
||||
Ok(())
|
||||
}
|
||||
|
762
src/parse.rs
762
src/parse.rs
@ -1,401 +1,381 @@
|
||||
use crate::ast::{AstNode, AstNodeKind};
|
||||
use nyst::Parser;
|
||||
use ron::{from_str, to_string};
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Token {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Bang,
|
||||
Quote(char),
|
||||
Parenthesis { closing: bool },
|
||||
Whitespace(String),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub fn parse<T: Into<String>>(src: T) -> AstNode {
|
||||
let src: String = src.into();
|
||||
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);
|
||||
let statement = ignored
|
||||
.optional()
|
||||
.ignore()
|
||||
.and(Parser::custom(parse_statement));
|
||||
let parse_program = statement
|
||||
.clone()
|
||||
.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()
|
||||
#[derive(Debug)]
|
||||
pub enum InterpreterError {
|
||||
/// Error parsing source
|
||||
ParseError(String),
|
||||
/// Unexpected EOF
|
||||
UnexpectedEOF,
|
||||
}
|
||||
fn parse_statement(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 function = Parser::regex(r"function\b").or(ignored.clone());
|
||||
let return_token = token(r"return\b");
|
||||
let semicolon = token(r"[;]");
|
||||
let if_token = token(r"if\b");
|
||||
let else_token = token(r"else\b");
|
||||
let left_paren = token(r"[(]");
|
||||
let right_paren = token(r"[)]");
|
||||
let left_brace = token(r"[{]");
|
||||
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)?);
|
||||
|
||||
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;
|
||||
}
|
||||
Ok(to_string(&AstNode::import(
|
||||
num_import_fn_args,
|
||||
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(),
|
||||
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_bool(chars: &[char]) -> Result<(Token, usize), ()> {
|
||||
if chars.len() >= 5 && chars[0..5] == ['f', 'a', 'l', 's', 'e'] {
|
||||
Ok((Token::Boolean(false), 5))
|
||||
} else if chars.len() >= 4 && chars[0..4] == ['t', 'r', 'u', 'e'] {
|
||||
Ok((Token::Boolean(true), 4))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
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
|
||||
))
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
let operation = tokens[index].clone();
|
||||
index += 1;
|
||||
let (right, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
|
||||
index += tokens_consumed;
|
||||
value = match operation {
|
||||
Token::Star => AstNode::Multiply {
|
||||
left: Box::new(value),
|
||||
right: Box::new(right),
|
||||
},
|
||||
Token::Slash => AstNode::Divide {
|
||||
left: Box::new(value),
|
||||
right: Box::new(right),
|
||||
},
|
||||
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())),
|
||||
};
|
||||
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((value, index))
|
||||
}
|
||||
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;
|
||||
}
|
||||
Ok(to_string(&AstNode::block(statements))?)
|
||||
});
|
||||
let args = identifier
|
||||
.clone()
|
||||
.and(
|
||||
comma
|
||||
.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());
|
||||
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()))
|
||||
}
|
||||
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.
|
||||
let mut vars = vec![];
|
||||
let mut others = vec![];
|
||||
for node in &body.subnodes {
|
||||
match node.kind {
|
||||
AstNodeKind::VariableDefinition => {
|
||||
vars.push(AstNode::variable_declaration(node.value.clone()));
|
||||
others.push(AstNode::assign(
|
||||
node.value.clone(),
|
||||
node.subnodes[0].clone(),
|
||||
))
|
||||
},
|
||||
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));
|
||||
}
|
||||
_ => others.push(node.clone()),
|
||||
}
|
||||
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()))
|
||||
}
|
||||
vars.append(&mut others);
|
||||
body.subnodes = vars;
|
||||
Ok(to_string(&AstNode::function_definition(
|
||||
name, parameters, body,
|
||||
))?)
|
||||
});
|
||||
return_statement
|
||||
.clone()
|
||||
.or(import_statement.clone())
|
||||
.or(if_statement.clone())
|
||||
.or(while_statement.clone())
|
||||
.or(var_statement.clone())
|
||||
.or(assignment_statement.clone())
|
||||
.or(block_statement.clone())
|
||||
.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());
|
||||
let comparison = infix(equal.clone().or(not_equal.clone()), sum.clone());
|
||||
comparison.parse(src)
|
||||
}
|
||||
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