Basic pest parser

main
Julius 2022-07-03 15:17:37 +02:00
parent fc942b9bc0
commit df3f0bf0a7
Signed by: j00lz
GPG Key ID: AF241B0AA237BBA2
12 changed files with 945 additions and 139 deletions

175
Cargo.lock generated
View File

@ -2,6 +2,181 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
]
[[package]]
name = "proc-macro2"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sunflower"
version = "0.1.0"
dependencies = [
"pest",
"pest_derive",
]
[[package]]
name = "syn"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-ident"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"

View File

@ -6,3 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pest = "2.1.3"
pest_derive = "2.1.0"

View File

@ -26,6 +26,10 @@ impl Env<'_> {
}
}
pub fn set_here(&self, name: &str, value: Value) {
self.values.write().unwrap().insert(name.to_string(), value);
}
pub fn set(&self, name: &str, value: impl Into<Value>) {
let value = value.into();
if !self.set_recursive(name, &value) {

View File

@ -1,3 +1,5 @@
use std::str::FromStr;
use crate::{env::Env, function::FunctionMap, value::Value};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -6,6 +8,7 @@ pub enum BinaryOperation {
Sub,
Mul,
Div,
Mod,
LessThan,
GreaterThan,
LessThanOrEqual,
@ -14,6 +17,27 @@ pub enum BinaryOperation {
NotEqual,
}
impl FromStr for BinaryOperation {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"+" => Ok(BinaryOperation::Add),
"-" => Ok(BinaryOperation::Sub),
"*" => Ok(BinaryOperation::Mul),
"/" => Ok(BinaryOperation::Div),
"%" => Ok(BinaryOperation::Mod),
"<" => Ok(BinaryOperation::LessThan),
">" => Ok(BinaryOperation::GreaterThan),
"<=" => Ok(BinaryOperation::LessThanOrEqual),
">=" => Ok(BinaryOperation::GreaterThanOrEqual),
"==" => Ok(BinaryOperation::Equal),
"!=" => Ok(BinaryOperation::NotEqual),
_ => Err("Invalid binary operation"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expression {
Constant(Value),
@ -27,9 +51,13 @@ impl Expression {
match self {
Expression::Constant(v) => v.clone(),
Expression::Variable(name) => e.get(name).unwrap_or_default(),
Expression::Call(name, args) => {
f.call(name, e, &args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>()).unwrap()
},
Expression::Call(name, args) => f
.call(
name,
e,
&args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>(),
)
.unwrap(),
Expression::Binary(lhs, op, rhs) => {
let lhs = lhs.eval(e, f);
let rhs = rhs.eval(e, f);
@ -38,6 +66,7 @@ impl Expression {
BinaryOperation::Sub => lhs - rhs,
BinaryOperation::Mul => lhs * rhs,
BinaryOperation::Div => lhs / rhs,
BinaryOperation::Mod => lhs % rhs,
BinaryOperation::LessThan => (lhs < rhs).into(),
BinaryOperation::GreaterThan => (lhs > rhs).into(),
BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(),

77
src/grammar.pest Normal file
View File

@ -0,0 +1,77 @@
WHITESPACE = _{ " " | "\t" | NEWLINE }
number = @{
"-"?
~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)
~ ("." ~ ASCII_DIGIT*)?
~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?
}
string = ${ "\"" ~ inner ~ "\"" }
inner = @{ character* }
character = {
!("\"" | "\\") ~ ANY
| "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
| "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}
char = ${ "\'" ~ character ~ "\'" }
boolean = { "true" | "false" }
identifier = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* }
call = ${
identifier ~ call_params
}
call_params = !{ "(" ~ (expression ~ ","?)* ~ ")" }
parens = { "(" ~ expression ~ ")" }
const_value = { number | string | char | boolean }
value = { const_value | call | identifier | parens }
expression = { equality }
equality = { inequality ~ ((equals | not_equals) ~ inequality)? }
inequality = { sum ~ ((less_than | greater_than | less_than_or_equal | greater_than_or_equal) ~ sum)? }
sum = { product ~ ((plus | minus) ~ product)* }
product = { value ~ ((times | divide | modulo) ~ value)* }
root_statement = {
SOI ~ statement* ~ EOI
}
statement = { ( assignment | assignment_let | expression ) ~ ";" | while_block | if_block | block }
block = { "{" ~ statement* ~ "}" }
assignment_let = { "let" ~ identifier ~ "=" ~ expression }
assignment = { identifier ~ "=" ~ expression }
while_block = { "while" ~ expression ~ block }
if_block = { "if" ~ expression ~ block ~ ("else if" ~ expression ~ block)* ~ ("else" ~ block)? }
equals = { "==" }
not_equals = { "!=" }
less_than = { "<" }
greater_than = { ">" }
less_than_or_equal = { "<=" }
greater_than_or_equal = { ">=" }
plus = { "+" }
minus = { "-" }
times = { "*" }
divide = { "/" }
modulo = { "%" }

200
src/lexer.rs Normal file
View File

@ -0,0 +1,200 @@
use std::hash::Hash;
use chumsky::{
prelude::{choice, filter, just, Simple},
text::{self, TextParser},
Parser,
};
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
POpen,
PClose,
SOpen,
SClose,
COpen,
CClose,
Comma,
Semicolon,
KeyWhile,
KeyReturn,
KeyLet,
Assign,
Operator(String),
Identifier(String),
ConstInt(i32),
ConstFloat(f32),
ConstString(String),
ConstBool(bool),
ConstChar(char),
}
impl Hash for Token {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}
impl Eq for Token {}
fn lexer() -> impl Parser<char, Vec<Token>, Error = Simple<char>> {
let escape = just('\\').ignore_then(
just('\\')
.or(just('/'))
.or(just('"'))
.or(just('b').to('\x08'))
.or(just('f').to('\x0C'))
.or(just('n').to('\n'))
.or(just('r').to('\r'))
.or(just('t').to('\t')),
);
let string = just('"')
.ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated())
.then_ignore(just('"'))
.collect::<String>()
.map(Token::ConstString);
let char = just('\'')
.ignore_then(chumsky::prelude::any())
.then_ignore(just('\''))
.map(Token::ConstChar);
let number = just('-')
.or_not()
.chain::<char, _, _>(text::int(10))
.collect::<String>()
.from_str()
.unwrapped()
.map(Token::ConstInt);
let float = just('-')
.or_not()
.chain(text::int(10))
.chain::<char, _, _>(just('.').chain(text::digits(10)))
.collect::<String>()
.from_str()
.unwrapped()
.map(Token::ConstFloat);
let op = just("+")
.or(just("-"))
.or(just("*"))
.or(just("/"))
.or(just("<"))
.or(just(">"))
.or(just("<="))
.or(just(">="))
.or(just("=="))
.or(just("!="))
.map(|op| Token::Operator(op.to_string()));
choice((
just("(").to(Token::POpen),
just(")").to(Token::PClose),
just("[").to(Token::SOpen),
just("]").to(Token::SClose),
just("{").to(Token::COpen),
just("}").to(Token::CClose),
just(",").to(Token::Comma),
just(";").to(Token::Semicolon),
just("=").to(Token::Assign),
op,
text::keyword("while").to(Token::KeyWhile),
text::keyword("return").to(Token::KeyReturn),
text::keyword("let").to(Token::KeyLet),
text::keyword("true").to(Token::ConstBool(true)),
text::keyword("false").to(Token::ConstBool(false)),
text::ident().map(Token::Identifier),
float,
number,
string,
char,
))
.padded()
.repeated()
}
#[cfg(test)]
mod tests {
use chumsky::Parser;
use crate::lexer::Token;
use super::lexer;
#[test]
fn parse_string() {
let x = "\"hello\"";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0], Token::ConstString("hello".to_string()));
}
#[test]
fn parse_char() {
let x = "'a'";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0], Token::ConstChar('a'));
}
#[test]
fn parse_int() {
let x = "123";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0], Token::ConstInt(123));
}
#[test]
fn parse_float() {
let x = "123.456";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0], Token::ConstFloat(123.456));
}
#[test]
fn parse_bool() {
let x = "true";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0], Token::ConstBool(true));
}
#[test]
fn parse_example_program() {
let x = r"
let x = 10;
let y = 200;
let z = x + y;
print(z);
";
let tokens = lexer().parse(x).unwrap();
assert_eq!(tokens.len(), 22);
assert_eq!(tokens[0], Token::KeyLet);
assert_eq!(tokens[1], Token::Identifier("x".to_string()));
assert_eq!(tokens[2], Token::Assign);
assert_eq!(tokens[3], Token::ConstInt(10));
assert_eq!(tokens[4], Token::Semicolon);
assert_eq!(tokens[5], Token::KeyLet);
assert_eq!(tokens[6], Token::Identifier("y".to_string()));
assert_eq!(tokens[7], Token::Assign);
assert_eq!(tokens[8], Token::ConstInt(200));
assert_eq!(tokens[9], Token::Semicolon);
assert_eq!(tokens[10], Token::KeyLet);
assert_eq!(tokens[11], Token::Identifier("z".to_string()));
assert_eq!(tokens[12], Token::Assign);
assert_eq!(tokens[13], Token::Identifier("x".to_string()));
assert_eq!(tokens[14], Token::Operator("+".to_string()));
assert_eq!(tokens[15], Token::Identifier("y".to_string()));
assert_eq!(tokens[16], Token::Semicolon);
assert_eq!(tokens[17], Token::Identifier("print".to_string()));
assert_eq!(tokens[18], Token::POpen);
assert_eq!(tokens[19], Token::Identifier("z".to_string()));
assert_eq!(tokens[20], Token::PClose);
assert_eq!(tokens[21], Token::Semicolon);
}
}

View File

@ -1,6 +1,9 @@
use std::fs::read_to_string;
use env::Env;
use expression::{BinaryOperation, Expression};
use function::{Function, FunctionMap, NativeFunction};
use pest::Parser;
use statement::Statement;
use value::Value;
@ -10,6 +13,9 @@ mod function;
mod macros;
mod statement;
mod value;
// mod lexer;
// mod parser;
mod pest_parser;
fn run(functions: FunctionMap, statements: &[Statement]) {
let env = Env::new();
@ -19,107 +25,117 @@ fn run(functions: FunctionMap, statements: &[Statement]) {
}
fn main() {
let statements = vec![
Statement::Assignment("a".to_string(), Expression::from(1)),
Statement::Assignment("b".to_string(), Expression::from(2)),
Statement::Assignment("c".to_string(), Expression::from(3)),
Statement::Print(Expression::Binary(
Box::new(Expression::Variable("a".to_string())),
BinaryOperation::Add,
Box::new(Expression::Variable("b".to_string())),
)),
Statement::Print(Expression::Binary(
Box::new(Expression::Variable("a".to_string())),
BinaryOperation::Add,
Box::new(Expression::Variable("c".to_string())),
)),
Statement::Print(Expression::Binary(
Box::new(Expression::Variable("b".to_string())),
BinaryOperation::Add,
Box::new(Expression::Variable("c".to_string())),
)),
];
run(FunctionMap::new(), &statements);
// let statements = vec![
// Statement::Assignment("a".to_string(), Expression::from(1)),
// Statement::Assignment("b".to_string(), Expression::from(2)),
// Statement::Assignment("c".to_string(), Expression::from(3)),
// Statement::Print(Expression::Binary(
// Box::new(Expression::Variable("a".to_string())),
// BinaryOperation::Add,
// Box::new(Expression::Variable("b".to_string())),
// )),
// Statement::Print(Expression::Binary(
// Box::new(Expression::Variable("a".to_string())),
// BinaryOperation::Add,
// Box::new(Expression::Variable("c".to_string())),
// )),
// Statement::Print(Expression::Binary(
// Box::new(Expression::Variable("b".to_string())),
// BinaryOperation::Add,
// Box::new(Expression::Variable("c".to_string())),
// )),
// ];
// run(FunctionMap::new(), &statements);
let while_test = vec![
Statement::Print("while test".to_string().into()),
Statement::Assignment("a".to_string(), Expression::from(1)),
Statement::While(
Expression::Binary(
Box::new(Expression::Variable("a".to_string())),
BinaryOperation::LessThan,
Box::new(10.into()),
),
vec![
Statement::Assignment(
"a".to_string(),
Expression::Binary(
Box::new(Expression::Variable("a".to_string())),
BinaryOperation::Add,
Box::new(1.into()),
),
),
Statement::Print(Expression::Variable("a".to_string())),
],
),
];
run(FunctionMap::new(), &while_test);
// let while_test = vec![
// Statement::Print("while test".to_string().into()),
// Statement::Assignment("a".to_string(), Expression::from(1)),
// Statement::While(
// Expression::Binary(
// Box::new(Expression::Variable("a".to_string())),
// BinaryOperation::LessThan,
// Box::new(10.into()),
// ),
// vec![
// Statement::Assignment(
// "a".to_string(),
// Expression::Binary(
// Box::new(Expression::Variable("a".to_string())),
// BinaryOperation::Add,
// Box::new(1.into()),
// ),
// ),
// Statement::Print(Expression::Variable("a".to_string())),
// ],
// ),
// ];
// run(FunctionMap::new(), &while_test);
{
let env = Env::new();
// {
// let env = Env::new();
let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]);
println!("{:?}", res);
}
// let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]);
// println!("{:?}", res);
// }
{
let z = || 100;
let env = Env::new();
let res = z.native_execute(&env, &[]);
println!("{:?}", res);
}
// {
// let z = || 100;
// let env = Env::new();
// let res = z.native_execute(&env, &[]);
// println!("{:?}", res);
// }
{
let z = |a: &Value| a.clone();
let env = Env::new();
let res = z.into_function().execute(&env, &FunctionMap::new(), &[]);
println!("{:?}", res);
}
// {
// let z = |a: &Value| a.clone();
// let env = Env::new();
// let res = z.into_function().execute(&env, &FunctionMap::new(), &[]);
// println!("{:?}", res);
// }
{
let z = |a: &String| a.clone();
let env = Env::new();
let res = z.native_execute(&env, &[100.into()]);
println!("{:?}", res);
}
// {
// let z = |a: &String| a.clone();
// let env = Env::new();
// let res = z.native_execute(&env, &[100.into()]);
// println!("{:?}", res);
// }
{
let v: Vec<Box<dyn Function>> = vec![
Box::new((|| 100).into_function()),
Box::new((|a: &i32| a + 1).into_function()),
];
for f in v {
let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]);
println!("{:?}", r);
}
}
// {
// let v: Vec<Box<dyn Function>> = vec![
// Box::new((|| 100).into_function()),
// Box::new((|a: &i32| a + 1).into_function()),
// ];
// for f in v {
// let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]);
// println!("{:?}", r);
// }
// }
// let mut m: FunctionMap = Default::default();
// m.insert_native("print", |a: &Value| {
// println!("{}", a);
// });
// m.insert_native("waluigi", || "Waluigi");
// m.insert_interpreter("call_print", vec!["what".to_string()], vec![
// Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])),
// ]);
// run(
// m,
// &vec![Statement::Expression(Expression::Call(
// "call_print".to_string(),
// vec![Expression::Call("waluigi".to_string(), vec![])],
// ))],
// );
let f = read_to_string("./test.foo").unwrap();
let mut m: FunctionMap = Default::default();
m.insert_native("print", |a: &Value| {
println!("{}", a);
});
m.insert_native("waluigi", || "Waluigi");
m.insert_interpreter("call_print", vec!["what".to_string()], vec![
Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])),
]);
run(
m,
&vec![Statement::Expression(Expression::Call(
"call_print".to_string(),
vec![Expression::Call("waluigi".to_string(), vec![])],
))],
);
let res = pest_parser::parse(f);
let env = &Env::new();
res.eval(env, &m);
}

88
src/parser.rs Normal file
View File

@ -0,0 +1,88 @@
use chumsky::{prelude::*, Parser};
use crate::{
expression::{BinaryOperation, Expression},
lexer::Token,
statement::Statement,
value::Value,
};
fn parser() -> impl Parser<Token, Vec<Statement>, Error = Simple<Token>> {
let operator = filter_map(|span, a| match a {
Token::Operator(ref op) => match op.as_str() {
"+" => Ok(BinaryOperation::Add),
"-" => Ok(BinaryOperation::Sub),
"*" => Ok(BinaryOperation::Mul),
"/" => Ok(BinaryOperation::Div),
"<" => Ok(BinaryOperation::LessThan),
">" => Ok(BinaryOperation::GreaterThan),
"<=" => Ok(BinaryOperation::LessThanOrEqual),
">=" => Ok(BinaryOperation::GreaterThanOrEqual),
"==" => Ok(BinaryOperation::Equal),
"!=" => Ok(BinaryOperation::NotEqual),
_ => Err(Simple::expected_input_found(span, None, Some(a))),
},
_ => Err(Simple::expected_input_found(span, None, Some(a))),
});
let tokens = filter_map(|span, a| match a {
Token::ConstInt(i) => Ok(Expression::Constant(Value::Int(i))),
Token::ConstFloat(f) => Ok(Expression::Constant(Value::Float(f))),
Token::ConstString(s) => Ok(Expression::Constant(Value::String(s))),
Token::ConstBool(b) => Ok(Expression::Constant(Value::Bool(b))),
Token::ConstChar(c) => Ok(Expression::Constant(Value::Char(c))),
_ => Err(chumsky::error::Error::expected_input_found(
span,
None,
Some(a),
)),
});
let expr = recursive(|rec| {
choice((
tokens
.then(operator)
.then(rec)
.map(|((a, b), c)| Expression::Binary(Box::new(a), b, Box::new(c))),
tokens,
))
});
let val = expr.map(Statement::Expression);
val.repeated()
}
#[cfg(test)]
mod tests {
use chumsky::Parser;
use crate::{
expression::{BinaryOperation, Expression},
lexer::Token,
parser::parser,
statement::Statement,
value::Value,
};
#[test]
fn parse_int() {
let input = vec![Token::ConstInt(10)];
let expected = vec![Statement::Expression(Expression::Constant(Value::Int(10)))];
let result = parser().parse(input).unwrap();
assert_eq!(result, expected);
}
#[test]
fn parse_10_plus_10() {
let input = vec![
Token::ConstInt(10),
Token::Operator("+".into()),
Token::ConstInt(10),
];
let expected = vec![Statement::Expression(Expression::Binary(
Box::new(Expression::Constant(Value::Int(10))),
BinaryOperation::Add,
Box::new(Expression::Constant(Value::Int(10))),
))];
let result = parser().parse(input).unwrap();
assert_eq!(result, expected);
}
}

221
src/pest_parser.rs Normal file
View File

@ -0,0 +1,221 @@
use pest::{iterators::Pair, Parser};
use pest_derive::Parser;
use crate::{
expression::{BinaryOperation, Expression},
statement::Statement,
value::Value,
};
#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct LangParser;
pub fn parse<S: ToString>(source: S) -> Statement {
let source = source.to_string();
let pairs = LangParser::parse(Rule::root_statement, &source).unwrap();
handle_rules(pairs)
}
pub fn parser_test() {
// let p = LangParser::parse(Rule::number, "-273.15").unwrap();
// println!("{:?}", p);
// let p = LangParser::parse(Rule::number, "25565");
// println!("{:?}", p);
// let str_test = LangParser::parse(Rule::string, "\"hello\"").unwrap();
// println!("{:?}", str_test);
// let str_test = LangParser::parse(Rule::char, "'a'").unwrap();
// println!("{:?}", str_test);
// let foo = LangParser::parse(Rule::statement, "10 + foo + bar(20) * 40;").unwrap();
// println!("{:?}", foo);
// let bar = "
// let x = 10;
// let y = 20;
// let z = x + y;
// print(z);
// while (x < y) {
// x = x + 1;
// }
// let x = x + \"lmao\";
// ";
// let bar = LangParser::parse(Rule::root_statement, bar);
// println!("{:?}", bar);
let baz = "
if x {
a;
} else if y {
b;
} else {
c;
}";
let baz = LangParser::parse(Rule::root_statement, baz);
println!("{:?}", baz);
let x = "x == 10;";
let x = LangParser::parse(Rule::statement, x);
handle_rules(x.unwrap());
}
fn handle_rules(mut p: pest::iterators::Pairs<Rule>) -> Statement {
fn parse_statement(pair: Pair<Rule>) -> Statement {
match pair.as_rule() {
Rule::root_statement => {
Statement::Body(pair.into_inner().map(parse_statement).collect())
}
Rule::while_block => {
let mut inner = pair.into_inner();
let expr = inner.next().unwrap();
let body = inner.next().unwrap();
Statement::While(parse_expression(expr), Box::new(parse_statement(body)))
}
Rule::if_block => {
let mut inner = pair.into_inner().into_iter();
let mut the_list = vec![];
let mut the_else = None;
while let Some(expr) = inner.next() {
if expr.as_rule() == Rule::expression {
let body = inner.next().unwrap();
the_list.push((parse_expression(expr), parse_statement(body)));
} else {
the_else = Some(Box::new(parse_statement(expr)));
}
}
Statement::If(the_list, the_else)
}
Rule::assignment => {
let mut inner = pair.into_inner();
let name = inner.next().unwrap();
let expr = inner.next().unwrap();
Statement::Assignment(name.as_str().to_string(), parse_expression(expr), false)
}
Rule::assignment_let => {
let mut inner = pair.into_inner();
let name = inner.next().unwrap();
let expr = inner.next().unwrap();
Statement::Assignment(name.as_str().to_string(), parse_expression(expr), true)
}
Rule::expression => Statement::Expression(parse_expression(pair)),
Rule::statement => {
let inner = pair.into_inner().next().unwrap();
parse_statement(inner)
}
Rule::EOI => Statement::Body(vec![]),
x => unreachable!("How did we get to {:?}", x),
}
}
fn parse_expression(pair: Pair<Rule>) -> Expression {
match pair.as_rule() {
Rule::expression => {
let mut inner = pair.into_inner();
parse_expression(inner.next().unwrap())
}
Rule::equality => {
let mut inner = pair.into_inner();
let lhs = parse_expression(inner.next().unwrap());
if let Some(op) = inner.next() {
let rhs = parse_expression(inner.next().unwrap());
Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs))
} else {
lhs
}
}
Rule::inequality => {
let mut inner = pair.into_inner();
let lhs = parse_expression(inner.next().unwrap());
if let Some(op) = inner.next() {
let rhs = parse_expression(inner.next().unwrap());
Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs))
} else {
lhs
}
}
Rule::sum => {
let mut inner = pair.into_inner();
let lhs = parse_expression(inner.next().unwrap());
if let Some(op) = inner.next() {
let rhs = parse_expression(inner.next().unwrap());
Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs))
} else {
lhs
}
}
Rule::product => {
let mut inner = pair.into_inner();
let lhs = parse_expression(inner.next().unwrap());
if let Some(op) = inner.next() {
let rhs = parse_expression(inner.next().unwrap());
Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs))
} else {
lhs
}
}
Rule::value => {
let inner = pair.into_inner().next().unwrap();
parse_value(inner)
}
_ => unreachable!(),
}
}
fn parse_value(pair: Pair<Rule>) -> Expression {
match pair.as_rule() {
Rule::const_value => {
Expression::Constant(parse_const_value(pair.into_inner().next().unwrap()))
}
Rule::identifier => Expression::Variable(pair.as_str().to_string()),
Rule::call => {
let mut inner = pair.into_inner();
let name = inner.next().unwrap();
let args = inner
.next()
.unwrap()
.into_inner()
.map(parse_expression)
.collect();
Expression::Call(name.as_str().to_string(), args)
}
Rule::parens => {
let mut inner = pair.into_inner();
parse_expression(inner.next().unwrap())
}
_ => unreachable!(),
}
}
fn parse_const_value(pair: Pair<Rule>) -> Value {
match pair.as_rule() {
Rule::number => {
if let Ok(f) = pair.as_str().parse::<i32>() {
Value::Int(f)
} else if let Ok(f) = pair.as_str().parse::<f32>() {
Value::Float(f)
} else {
unreachable!()
}
}
Rule::string => Value::String(pair.as_str().to_string()),
Rule::boolean => Value::Bool(pair.as_str() == "true"),
Rule::char => Value::Char(pair.as_str().chars().next().unwrap()),
_ => unreachable!(),
}
}
let z = p.next().unwrap();
parse_statement(z)
}
#[cfg(test)]
mod tests {
use pest::Parser;
use super::{LangParser, Rule};
#[test]
fn foo() {}
}

View File

@ -1,34 +1,55 @@
use crate::{env::Env, expression::Expression, function::FunctionMap};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Assignment(String, Expression),
Assignment(String, Expression, bool),
Expression(Expression),
Print(Expression),
While(Expression, Vec<Statement>),
While(Expression, Box<Statement>),
If(Vec<(Expression, Statement)>, Option<Box<Statement>>),
Body(Vec<Statement>),
}
impl Statement {
pub fn eval(&self, env: &Env, f: &FunctionMap) {
match self {
Statement::Assignment(name, expr) => {
Statement::Assignment(name, expr, explicit_let) => {
let value = expr.eval(env, f);
env.set(name, value);
if *explicit_let {
env.set_here(name, value);
} else {
env.set(name, value);
}
}
Statement::Expression(expr) => {
expr.eval(env, f);
}
Statement::Print(expr) => {
let value = expr.eval(env, f);
println!("{}", value);
}
Statement::While(expr, body) => {
while expr.eval(env, f).is_truthy() {
let body_env = Env::new_with_parent(env);
for stmt in body.iter() {
stmt.eval(&body_env, f);
body.eval(&body_env, f);
}
}
Statement::If(if_side, else_side) => {
let mut did_if = false;
for (expr, body) in if_side {
if expr.eval(env, f).is_truthy() {
body.eval(env, f);
did_if = true;
break;
}
}
if !did_if {
if let Some(else_side) = else_side {
else_side.eval(env, f);
}
}
}
Statement::Body(body) => {
let body_env = Env::new_with_parent(env);
for stmt in body.iter() {
stmt.eval(&body_env, f);
}
}
}
}

View File

@ -1,6 +1,6 @@
use std::{
fmt::Display,
ops::{Add, Div, Mul, Sub},
ops::{Add, Div, Mul, Sub, Rem},
};
#[derive(Debug, Clone, PartialEq, Default)]
@ -17,39 +17,6 @@ pub enum Value {
impl Eq for Value {}
// impl Value {
// pub fn as_int(&self) -> Option<i32> {
// match self {
// Value::Int(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_float(&self) -> Option<f32> {
// match self {
// Value::Float(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_string(&self) -> Option<String> {
// match self {
// Value::String(v) => Some(v.clone()),
// _ => None,
// }
// }
// pub fn as_bool(&self) -> Option<bool> {
// match self {
// Value::Bool(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_char(&self) -> Option<char> {
// match self {
// Value::Char(v) => Some(*v),
// _ => None,
// }
// }
// }
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
@ -95,6 +62,7 @@ math_trait!(Add, add);
math_trait!(Sub, sub);
math_trait!(Mul, mul);
math_trait!(Div, div);
math_trait!(Rem, rem);
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

4
test.foo Normal file
View File

@ -0,0 +1,4 @@
let x = 10;
let y = x-5;
print(y);