Make basic stuff not suck

This commit is contained in:
Garen Tyler 2022-03-25 21:29:48 -06:00
parent 257c948c3b
commit 643b4c8538
No known key found for this signature in database
GPG Key ID: E3BF83D66394FD92
9 changed files with 543 additions and 662 deletions

57
Cargo.lock generated
View File

@ -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"

View File

@ -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" }

1
out.log Normal file
View File

@ -0,0 +1 @@
tokens: [String("a"), Plus, String("b")]

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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(())
}

View File

@ -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>>(&params[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)
}
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())),
}
}
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(&current)?)
})
};
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)
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(())
}

View File

@ -1,4 +1 @@
import log 1 0;
function main() {
log(3);
}
"a" + "b"

13
test.txt Normal file
View 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