Basic pest parser
This commit is contained in:
parent
fc942b9bc0
commit
df3f0bf0a7
175
Cargo.lock
generated
175
Cargo.lock
generated
|
@ -2,6 +2,181 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "sunflower"
|
name = "sunflower"
|
||||||
version = "0.1.0"
|
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"
|
||||||
|
|
|
@ -6,3 +6,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
pest = "2.1.3"
|
||||||
|
pest_derive = "2.1.0"
|
||||||
|
|
|
@ -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>) {
|
pub fn set(&self, name: &str, value: impl Into<Value>) {
|
||||||
let value = value.into();
|
let value = value.into();
|
||||||
if !self.set_recursive(name, &value) {
|
if !self.set_recursive(name, &value) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{env::Env, function::FunctionMap, value::Value};
|
use crate::{env::Env, function::FunctionMap, value::Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -6,6 +8,7 @@ pub enum BinaryOperation {
|
||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
|
Mod,
|
||||||
LessThan,
|
LessThan,
|
||||||
GreaterThan,
|
GreaterThan,
|
||||||
LessThanOrEqual,
|
LessThanOrEqual,
|
||||||
|
@ -14,6 +17,27 @@ pub enum BinaryOperation {
|
||||||
NotEqual,
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
|
@ -27,9 +51,13 @@ impl Expression {
|
||||||
match self {
|
match self {
|
||||||
Expression::Constant(v) => v.clone(),
|
Expression::Constant(v) => v.clone(),
|
||||||
Expression::Variable(name) => e.get(name).unwrap_or_default(),
|
Expression::Variable(name) => e.get(name).unwrap_or_default(),
|
||||||
Expression::Call(name, args) => {
|
Expression::Call(name, args) => f
|
||||||
f.call(name, e, &args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>()).unwrap()
|
.call(
|
||||||
},
|
name,
|
||||||
|
e,
|
||||||
|
&args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
Expression::Binary(lhs, op, rhs) => {
|
Expression::Binary(lhs, op, rhs) => {
|
||||||
let lhs = lhs.eval(e, f);
|
let lhs = lhs.eval(e, f);
|
||||||
let rhs = rhs.eval(e, f);
|
let rhs = rhs.eval(e, f);
|
||||||
|
@ -38,6 +66,7 @@ impl Expression {
|
||||||
BinaryOperation::Sub => lhs - rhs,
|
BinaryOperation::Sub => lhs - rhs,
|
||||||
BinaryOperation::Mul => lhs * rhs,
|
BinaryOperation::Mul => lhs * rhs,
|
||||||
BinaryOperation::Div => lhs / rhs,
|
BinaryOperation::Div => lhs / rhs,
|
||||||
|
BinaryOperation::Mod => lhs % rhs,
|
||||||
BinaryOperation::LessThan => (lhs < rhs).into(),
|
BinaryOperation::LessThan => (lhs < rhs).into(),
|
||||||
BinaryOperation::GreaterThan => (lhs > rhs).into(),
|
BinaryOperation::GreaterThan => (lhs > rhs).into(),
|
||||||
BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(),
|
BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(),
|
||||||
|
|
77
src/grammar.pest
Normal file
77
src/grammar.pest
Normal 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
200
src/lexer.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
196
src/main.rs
196
src/main.rs
|
@ -1,6 +1,9 @@
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use env::Env;
|
use env::Env;
|
||||||
use expression::{BinaryOperation, Expression};
|
use expression::{BinaryOperation, Expression};
|
||||||
use function::{Function, FunctionMap, NativeFunction};
|
use function::{Function, FunctionMap, NativeFunction};
|
||||||
|
use pest::Parser;
|
||||||
use statement::Statement;
|
use statement::Statement;
|
||||||
use value::Value;
|
use value::Value;
|
||||||
|
|
||||||
|
@ -10,6 +13,9 @@ mod function;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod statement;
|
mod statement;
|
||||||
mod value;
|
mod value;
|
||||||
|
// mod lexer;
|
||||||
|
// mod parser;
|
||||||
|
mod pest_parser;
|
||||||
|
|
||||||
fn run(functions: FunctionMap, statements: &[Statement]) {
|
fn run(functions: FunctionMap, statements: &[Statement]) {
|
||||||
let env = Env::new();
|
let env = Env::new();
|
||||||
|
@ -19,107 +25,117 @@ fn run(functions: FunctionMap, statements: &[Statement]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let statements = vec![
|
// let statements = vec![
|
||||||
Statement::Assignment("a".to_string(), Expression::from(1)),
|
// Statement::Assignment("a".to_string(), Expression::from(1)),
|
||||||
Statement::Assignment("b".to_string(), Expression::from(2)),
|
// Statement::Assignment("b".to_string(), Expression::from(2)),
|
||||||
Statement::Assignment("c".to_string(), Expression::from(3)),
|
// Statement::Assignment("c".to_string(), Expression::from(3)),
|
||||||
Statement::Print(Expression::Binary(
|
// Statement::Print(Expression::Binary(
|
||||||
Box::new(Expression::Variable("a".to_string())),
|
// Box::new(Expression::Variable("a".to_string())),
|
||||||
BinaryOperation::Add,
|
// BinaryOperation::Add,
|
||||||
Box::new(Expression::Variable("b".to_string())),
|
// Box::new(Expression::Variable("b".to_string())),
|
||||||
)),
|
// )),
|
||||||
Statement::Print(Expression::Binary(
|
// Statement::Print(Expression::Binary(
|
||||||
Box::new(Expression::Variable("a".to_string())),
|
// Box::new(Expression::Variable("a".to_string())),
|
||||||
BinaryOperation::Add,
|
// BinaryOperation::Add,
|
||||||
Box::new(Expression::Variable("c".to_string())),
|
// Box::new(Expression::Variable("c".to_string())),
|
||||||
)),
|
// )),
|
||||||
Statement::Print(Expression::Binary(
|
// Statement::Print(Expression::Binary(
|
||||||
Box::new(Expression::Variable("b".to_string())),
|
// Box::new(Expression::Variable("b".to_string())),
|
||||||
BinaryOperation::Add,
|
// BinaryOperation::Add,
|
||||||
Box::new(Expression::Variable("c".to_string())),
|
// Box::new(Expression::Variable("c".to_string())),
|
||||||
)),
|
// )),
|
||||||
];
|
// ];
|
||||||
run(FunctionMap::new(), &statements);
|
// run(FunctionMap::new(), &statements);
|
||||||
|
|
||||||
let while_test = vec![
|
// let while_test = vec![
|
||||||
Statement::Print("while test".to_string().into()),
|
// Statement::Print("while test".to_string().into()),
|
||||||
Statement::Assignment("a".to_string(), Expression::from(1)),
|
// Statement::Assignment("a".to_string(), Expression::from(1)),
|
||||||
Statement::While(
|
// Statement::While(
|
||||||
Expression::Binary(
|
// Expression::Binary(
|
||||||
Box::new(Expression::Variable("a".to_string())),
|
// Box::new(Expression::Variable("a".to_string())),
|
||||||
BinaryOperation::LessThan,
|
// BinaryOperation::LessThan,
|
||||||
Box::new(10.into()),
|
// Box::new(10.into()),
|
||||||
),
|
// ),
|
||||||
vec![
|
// vec![
|
||||||
Statement::Assignment(
|
// Statement::Assignment(
|
||||||
"a".to_string(),
|
// "a".to_string(),
|
||||||
Expression::Binary(
|
// Expression::Binary(
|
||||||
Box::new(Expression::Variable("a".to_string())),
|
// Box::new(Expression::Variable("a".to_string())),
|
||||||
BinaryOperation::Add,
|
// BinaryOperation::Add,
|
||||||
Box::new(1.into()),
|
// Box::new(1.into()),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
Statement::Print(Expression::Variable("a".to_string())),
|
// Statement::Print(Expression::Variable("a".to_string())),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
];
|
// ];
|
||||||
run(FunctionMap::new(), &while_test);
|
// 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)]);
|
// let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]);
|
||||||
println!("{:?}", res);
|
// println!("{:?}", res);
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
let z = || 100;
|
// let z = || 100;
|
||||||
let env = Env::new();
|
// let env = Env::new();
|
||||||
let res = z.native_execute(&env, &[]);
|
// let res = z.native_execute(&env, &[]);
|
||||||
println!("{:?}", res);
|
// println!("{:?}", res);
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
let z = |a: &Value| a.clone();
|
// let z = |a: &Value| a.clone();
|
||||||
let env = Env::new();
|
// let env = Env::new();
|
||||||
let res = z.into_function().execute(&env, &FunctionMap::new(), &[]);
|
// let res = z.into_function().execute(&env, &FunctionMap::new(), &[]);
|
||||||
println!("{:?}", res);
|
// println!("{:?}", res);
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
let z = |a: &String| a.clone();
|
// let z = |a: &String| a.clone();
|
||||||
let env = Env::new();
|
// let env = Env::new();
|
||||||
let res = z.native_execute(&env, &[100.into()]);
|
// let res = z.native_execute(&env, &[100.into()]);
|
||||||
println!("{:?}", res);
|
// println!("{:?}", res);
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
let v: Vec<Box<dyn Function>> = vec![
|
// let v: Vec<Box<dyn Function>> = vec![
|
||||||
Box::new((|| 100).into_function()),
|
// Box::new((|| 100).into_function()),
|
||||||
Box::new((|a: &i32| a + 1).into_function()),
|
// Box::new((|a: &i32| a + 1).into_function()),
|
||||||
];
|
// ];
|
||||||
for f in v {
|
// for f in v {
|
||||||
let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]);
|
// let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]);
|
||||||
println!("{:?}", r);
|
// 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();
|
let mut m: FunctionMap = Default::default();
|
||||||
m.insert_native("print", |a: &Value| {
|
m.insert_native("print", |a: &Value| {
|
||||||
println!("{}", a);
|
println!("{}", a);
|
||||||
});
|
});
|
||||||
|
|
||||||
m.insert_native("waluigi", || "Waluigi");
|
m.insert_native("waluigi", || "Waluigi");
|
||||||
|
let res = pest_parser::parse(f);
|
||||||
m.insert_interpreter("call_print", vec!["what".to_string()], vec![
|
let env = &Env::new();
|
||||||
Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])),
|
res.eval(env, &m);
|
||||||
]);
|
|
||||||
|
|
||||||
run(
|
|
||||||
m,
|
|
||||||
&vec![Statement::Expression(Expression::Call(
|
|
||||||
"call_print".to_string(),
|
|
||||||
vec![Expression::Call("waluigi".to_string(), vec![])],
|
|
||||||
))],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
88
src/parser.rs
Normal file
88
src/parser.rs
Normal 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
221
src/pest_parser.rs
Normal 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() {}
|
||||||
|
}
|
|
@ -1,34 +1,55 @@
|
||||||
use crate::{env::Env, expression::Expression, function::FunctionMap};
|
use crate::{env::Env, expression::Expression, function::FunctionMap};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Assignment(String, Expression),
|
Assignment(String, Expression, bool),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
Print(Expression),
|
While(Expression, Box<Statement>),
|
||||||
While(Expression, Vec<Statement>),
|
If(Vec<(Expression, Statement)>, Option<Box<Statement>>),
|
||||||
|
Body(Vec<Statement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
pub fn eval(&self, env: &Env, f: &FunctionMap) {
|
pub fn eval(&self, env: &Env, f: &FunctionMap) {
|
||||||
match self {
|
match self {
|
||||||
Statement::Assignment(name, expr) => {
|
Statement::Assignment(name, expr, explicit_let) => {
|
||||||
let value = expr.eval(env, f);
|
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) => {
|
Statement::Expression(expr) => {
|
||||||
expr.eval(env, f);
|
expr.eval(env, f);
|
||||||
}
|
}
|
||||||
Statement::Print(expr) => {
|
|
||||||
let value = expr.eval(env, f);
|
|
||||||
println!("{}", value);
|
|
||||||
}
|
|
||||||
Statement::While(expr, body) => {
|
Statement::While(expr, body) => {
|
||||||
while expr.eval(env, f).is_truthy() {
|
while expr.eval(env, f).is_truthy() {
|
||||||
let body_env = Env::new_with_parent(env);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
src/value.rs
36
src/value.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::{Add, Div, Mul, Sub},
|
ops::{Add, Div, Mul, Sub, Rem},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
@ -17,39 +17,6 @@ pub enum Value {
|
||||||
|
|
||||||
impl Eq for 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 {
|
impl Value {
|
||||||
pub fn is_truthy(&self) -> bool {
|
pub fn is_truthy(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -95,6 +62,7 @@ math_trait!(Add, add);
|
||||||
math_trait!(Sub, sub);
|
math_trait!(Sub, sub);
|
||||||
math_trait!(Mul, mul);
|
math_trait!(Mul, mul);
|
||||||
math_trait!(Div, div);
|
math_trait!(Div, div);
|
||||||
|
math_trait!(Rem, rem);
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
Loading…
Reference in a new issue