From b4cb20b968678abce23072cbca11577de74d9fa7 Mon Sep 17 00:00:00 2001 From: annieversary Date: Wed, 22 Sep 2021 22:47:23 +0200 Subject: [PATCH] got parser kinda working --- .gitignore | 2 + Cargo.lock | 39 +++++++ Cargo.toml | 9 ++ src/ast.rs | 38 +++++++ src/main.rs | 10 ++ src/parser.rs | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 381 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/ast.rs create mode 100644 src/main.rs create mode 100644 src/parser.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0592392 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.DS_Store diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6788946 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6f6d3b3 --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..7416045 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,38 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Expr { + LNull, + LBool(bool), + LStr(String), + LNum(f64), + Var(Identifier), + Binary(BinOp, Box, Box), + Call(Identifier, Vec), + Receive(Box), +} + +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), + While(Expr, Vec), + Func(Identifier, Vec, Vec), + Return(Option), + Yield, + Spawn(Expr), + Send(Expr, Identifier), +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0f662e6 --- /dev/null +++ b/src/main.rs @@ -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!"); +} diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..3886c5b --- /dev/null +++ b/src/parser.rs @@ -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)> { + 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)"#; + } +}