got parser kinda working
This commit is contained in:
commit
b4cb20b968
6 changed files with 381 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.DS_Store
|
39
Cargo.lock
generated
Normal file
39
Cargo.lock
generated
Normal file
|
@ -0,0 +1,39 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsco"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "rsco"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = "7.0.0"
|
38
src/ast.rs
Normal file
38
src/ast.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Expr {
|
||||
LNull,
|
||||
LBool(bool),
|
||||
LStr(String),
|
||||
LNum(f64),
|
||||
Var(Identifier),
|
||||
Binary(BinOp, Box<Expr>, Box<Expr>),
|
||||
Call(Identifier, Vec<Expr>),
|
||||
Receive(Box<Expr>),
|
||||
}
|
||||
|
||||
pub type Identifier = String;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BinOp {
|
||||
Plus,
|
||||
Minus,
|
||||
Mult,
|
||||
Div,
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
}
|
||||
|
||||
pub enum Stmt {
|
||||
Expr(Expr),
|
||||
Var(Identifier, Expr),
|
||||
Assign(Identifier, Expr),
|
||||
If(Expr, Vec<Stmt>),
|
||||
While(Expr, Vec<Stmt>),
|
||||
Func(Identifier, Vec<Identifier>, Vec<Stmt>),
|
||||
Return(Option<Expr>),
|
||||
Yield,
|
||||
Spawn(Expr),
|
||||
Send(Expr, Identifier),
|
||||
}
|
10
src/main.rs
Normal file
10
src/main.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
mod ast;
|
||||
mod parser;
|
||||
|
||||
fn main() {
|
||||
let a = "hello( )";
|
||||
let (_rest, res) = parser::expr(a).unwrap();
|
||||
dbg!(res);
|
||||
|
||||
println!("Hello, world!");
|
||||
}
|
283
src/parser.rs
Normal file
283
src/parser.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{escaped, is_not},
|
||||
bytes::complete::{tag, take_until},
|
||||
character::complete::char,
|
||||
character::complete::multispace0,
|
||||
character::complete::{alpha1, alphanumeric1, digit1, one_of, space0},
|
||||
combinator::value,
|
||||
combinator::{cut, map, map_res, recognize},
|
||||
error::{context, convert_error, ContextError, ErrorKind, ParseError, VerboseError},
|
||||
multi::{fold_many0, many0, separated_list0},
|
||||
number::complete::double,
|
||||
sequence::pair,
|
||||
sequence::{delimited, preceded, terminated, tuple},
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
use crate::ast::*;
|
||||
|
||||
fn sc<'a, E>(i: &'a str) -> IResult<&'a str, (), E>
|
||||
where
|
||||
E: ParseError<&'a str>,
|
||||
{
|
||||
alt((value((), multispace0), pinline_comment, peol_comment))(i)
|
||||
}
|
||||
|
||||
fn pinline_comment<'a, E>(i: &'a str) -> IResult<&'a str, (), E>
|
||||
where
|
||||
E: ParseError<&'a str>,
|
||||
{
|
||||
value(
|
||||
(), // Output is thrown away.
|
||||
tuple((tag("/*"), take_until("*/"), tag("*/"))),
|
||||
)(i)
|
||||
}
|
||||
|
||||
fn peol_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (), E> {
|
||||
value(
|
||||
(), // Output is thrown away.
|
||||
pair(char('/'), is_not("\n\r")),
|
||||
)(i)
|
||||
}
|
||||
|
||||
fn braces<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
|
||||
where
|
||||
P: Parser<&'a str, O, E>,
|
||||
E: ParseError<&'a str>,
|
||||
{
|
||||
delimited(char('{'), a, char('}'))
|
||||
}
|
||||
|
||||
fn parens<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
|
||||
where
|
||||
P: Parser<&'a str, O, E>,
|
||||
E: ParseError<&'a str>,
|
||||
{
|
||||
delimited(
|
||||
tuple((tag("("), recognize(sc))),
|
||||
a,
|
||||
tuple((recognize(sc), tag(")"))),
|
||||
)
|
||||
}
|
||||
|
||||
fn identifier(input: &str) -> IResult<&str, &str> {
|
||||
recognize(pair(
|
||||
alt((alpha1, tag("_"))),
|
||||
many0(alt((alphanumeric1, tag("_")))),
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn parse_str<'a>(i: &'a str) -> IResult<&'a str, &'a str> {
|
||||
escaped(alphanumeric1, '\\', one_of("\"n\\"))(i)
|
||||
}
|
||||
|
||||
fn string<'a>(i: &'a str) -> IResult<&'a str, &'a str> {
|
||||
preceded(char('\"'), cut(terminated(parse_str, char('\"'))))(i)
|
||||
}
|
||||
|
||||
fn boolean<'a>(i: &'a str) -> IResult<&'a str, bool> {
|
||||
println!("boolean: {}", i);
|
||||
alt((map(tag("true"), |_| true), map(tag("false"), |_| false)))(i)
|
||||
}
|
||||
|
||||
fn call<'a>(i: &'a str) -> IResult<&'a str, (Identifier, Vec<Expr>)> {
|
||||
println!("call: {}", i);
|
||||
map(
|
||||
tuple((
|
||||
identifier,
|
||||
delimited(
|
||||
char('('),
|
||||
separated_list0(tuple((sc, tag(","), sc)), expr),
|
||||
char(')'),
|
||||
),
|
||||
)),
|
||||
|(i, v)| (i.to_string(), v),
|
||||
)(i)
|
||||
}
|
||||
|
||||
fn num(i: &str) -> IResult<&str, Expr> {
|
||||
println!("num: {}", i);
|
||||
map(double, Expr::LNum)(i)
|
||||
}
|
||||
|
||||
fn parens_bin(i: &str) -> IResult<&str, Expr> {
|
||||
delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i)
|
||||
}
|
||||
|
||||
fn factor_bin(i: &str) -> IResult<&str, Expr> {
|
||||
alt((delimited(space0, num, space0), parens_bin))(i)
|
||||
}
|
||||
|
||||
fn bin_less_greater(i: &str) -> IResult<&str, Expr> {
|
||||
let (i, init) = factor_bin(i)?;
|
||||
|
||||
fold_many0(
|
||||
pair(alt((char('<'), char('>'))), factor_bin),
|
||||
move || init.clone(),
|
||||
|acc, (op, val): (char, Expr)| {
|
||||
if op == '<' {
|
||||
Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val))
|
||||
} else {
|
||||
Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val))
|
||||
}
|
||||
},
|
||||
)(i)
|
||||
}
|
||||
fn bin_equal_not(i: &str) -> IResult<&str, Expr> {
|
||||
let (i, init) = bin_less_greater(i)?;
|
||||
|
||||
fold_many0(
|
||||
pair(alt((char('<'), char('>'))), bin_less_greater),
|
||||
move || init.clone(),
|
||||
|acc, (op, val): (char, Expr)| {
|
||||
if op == '<' {
|
||||
Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val))
|
||||
} else {
|
||||
Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val))
|
||||
}
|
||||
},
|
||||
)(i)
|
||||
}
|
||||
fn bin_mult_div(i: &str) -> IResult<&str, Expr> {
|
||||
let (i, init) = bin_equal_not(i)?;
|
||||
|
||||
fold_many0(
|
||||
pair(alt((char('*'), char('/'))), bin_equal_not),
|
||||
move || init.clone(),
|
||||
|acc, (op, val): (char, Expr)| {
|
||||
if op == '*' {
|
||||
Expr::Binary(BinOp::Mult, Box::new(acc), Box::new(val))
|
||||
} else {
|
||||
Expr::Binary(BinOp::Div, Box::new(acc), Box::new(val))
|
||||
}
|
||||
},
|
||||
)(i)
|
||||
}
|
||||
fn binary<'a>(i: &'a str) -> IResult<&'a str, Expr> {
|
||||
println!("binary: {}", i);
|
||||
let (i, init) = bin_mult_div(i)?;
|
||||
|
||||
fold_many0(
|
||||
pair(alt((char('+'), char('-'))), bin_mult_div),
|
||||
move || init.clone(),
|
||||
|acc, (op, val): (char, Expr)| {
|
||||
if op == '+' {
|
||||
Expr::Binary(BinOp::Plus, Box::new(acc), Box::new(val))
|
||||
} else {
|
||||
Expr::Binary(BinOp::Minus, Box::new(acc), Box::new(val))
|
||||
}
|
||||
},
|
||||
)(i)
|
||||
}
|
||||
fn receive<'a>(i: &'a str) -> IResult<&'a str, Expr> {
|
||||
map(pair(tag("<-"), expr), |(_, expr)| {
|
||||
Expr::Receive(Box::new(expr))
|
||||
})(i)
|
||||
}
|
||||
|
||||
pub fn expr<'a>(i: &'a str) -> IResult<&'a str, Expr> {
|
||||
println!("expr: {}", i);
|
||||
preceded(
|
||||
sc,
|
||||
alt((
|
||||
parens(expr),
|
||||
map(call, |(i, v)| Expr::Call(i, v)),
|
||||
map(identifier, |i| Expr::Var(i.to_string())),
|
||||
receive,
|
||||
binary,
|
||||
map(tag("null"), |_| Expr::LNull),
|
||||
map(string, |s| Expr::LStr(String::from(s))),
|
||||
num,
|
||||
)),
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn can_parse_string_lit() {
|
||||
let a = r#""hey" "#;
|
||||
let (rest, res) = string(a).unwrap();
|
||||
|
||||
assert_eq!(res, "hey");
|
||||
assert_eq!(rest, " ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_binary_op() {
|
||||
let a = "3 +3 + 6";
|
||||
let (_rest, res) = binary(a).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
Expr::Binary(
|
||||
BinOp::Plus,
|
||||
Box::new(Expr::Binary(
|
||||
BinOp::Plus,
|
||||
Box::new(Expr::LNum(3.0)),
|
||||
Box::new(Expr::LNum(3.0)),
|
||||
)),
|
||||
Box::new(Expr::LNum(6.0))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_empty_call() {
|
||||
let a = "hello()";
|
||||
let (rest, res) = expr(a).unwrap();
|
||||
|
||||
assert_eq!(res, Expr::Call("hello".to_string(), vec![]));
|
||||
assert_eq!(rest, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_call() {
|
||||
let a = "hello(1.0, 45 , yeah)";
|
||||
let (rest, res) = expr(a).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
Expr::Call(
|
||||
"hello".to_string(),
|
||||
vec![
|
||||
Expr::LNum(1.0),
|
||||
Expr::LNum(45.0),
|
||||
Expr::Var("yeah".to_string())
|
||||
]
|
||||
)
|
||||
);
|
||||
assert_eq!(rest, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_call_with_bin_op() {
|
||||
let a = "hello(1.0, 45 + 33, yeah)";
|
||||
let (rest, res) = expr(a).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
Expr::Call(
|
||||
"hello".to_string(),
|
||||
vec![
|
||||
Expr::LNum(1.0),
|
||||
Expr::Binary(
|
||||
BinOp::Plus,
|
||||
Box::new(Expr::LNum(45.0)),
|
||||
Box::new(Expr::LNum(33.0))
|
||||
),
|
||||
Expr::Var("yeah".to_string())
|
||||
]
|
||||
)
|
||||
);
|
||||
assert_eq!(rest, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse() {
|
||||
let a = r#"let hello = ("hey" + 0.5)"#;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue