Initial commit
Includes: runtime base from a previous project, syn(tax) module with parser and lexer Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
162
src/syn/ast.rs
Normal file
162
src/syn/ast.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Stmt {
|
||||
Expr(Expr),
|
||||
Assign,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Expr {
|
||||
Atom(Atom),
|
||||
Bin(Box<BinExpr>),
|
||||
Un(Box<UnExpr>),
|
||||
Access(Box<AccessExpr>),
|
||||
}
|
||||
|
||||
impl From<Atom> for Expr {
|
||||
fn from(other: Atom) -> Self {
|
||||
Expr::Atom(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BinExpr> for Expr {
|
||||
fn from(other: BinExpr) -> Self {
|
||||
Expr::Bin(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnExpr> for Expr {
|
||||
fn from(other: UnExpr) -> Self {
|
||||
Expr::Un(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AccessExpr> for Expr {
|
||||
fn from(other: AccessExpr) -> Self {
|
||||
Expr::Access(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BinOp {
|
||||
Plus,
|
||||
Minus,
|
||||
Times,
|
||||
Div,
|
||||
Eq,
|
||||
Neq,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BinExpr {
|
||||
pub lhs: Expr,
|
||||
pub op: BinOp,
|
||||
pub rhs: Expr,
|
||||
}
|
||||
|
||||
impl BinExpr {
|
||||
pub fn new_expr(lhs: Expr, op: BinOp, rhs: Expr) -> Expr {
|
||||
Self { lhs, op, rhs, }.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum UnOp {
|
||||
Plus,
|
||||
Minus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UnExpr {
|
||||
pub op: UnOp,
|
||||
pub expr: Expr,
|
||||
}
|
||||
|
||||
impl UnExpr {
|
||||
pub fn new_expr(op: UnOp, expr: Expr) -> Expr {
|
||||
Self { op, expr }.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AccessExpr {
|
||||
pub expr: Expr,
|
||||
pub access: Vec<Access>,
|
||||
}
|
||||
|
||||
impl AccessExpr {
|
||||
pub fn new_expr(expr: Expr, access: Vec<Access>) -> Expr {
|
||||
Self { expr, access, }.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Access {
|
||||
pub access: String,
|
||||
pub trailing: Vec<ExprTrail>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExprTrail {
|
||||
Call(Vec<Expr>),
|
||||
Index(Expr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Atom {
|
||||
Ident(String),
|
||||
Sym(String),
|
||||
Num(i64),
|
||||
String(String),
|
||||
}
|
||||
|
||||
pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<lrpar::LexParseError<u32>>> {
|
||||
use crate::syn::{lexer, parser};
|
||||
let lexerdef = lexer::lexerdef();
|
||||
let lexer = lexerdef.lexer(text);
|
||||
let (res, errors) = parser::parse(&lexer);
|
||||
|
||||
if !errors.is_empty() {
|
||||
return Err(errors)
|
||||
}
|
||||
|
||||
Ok(res.transpose().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn parse_expr(text: &str) -> Expr {
|
||||
let mut body = parse(text)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(body.len() == 1);
|
||||
let stmt = body.pop().unwrap();
|
||||
|
||||
if let Stmt::Expr(expr) = stmt {
|
||||
expr
|
||||
} else {
|
||||
panic!("{:?} parses to {:?} which is not a Stmt::Expr", text, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access() {
|
||||
assert_eq!(
|
||||
parse_expr("a.b.c()"),
|
||||
AccessExpr::new_expr(
|
||||
Atom::Ident("a".to_string()).into(),
|
||||
vec![
|
||||
Access { access: "b".to_string(), trailing: vec![], },
|
||||
Access { access: "c".to_string(), trailing: vec![ExprTrail::Call(vec![])], },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
27
src/syn/lexer.l
Normal file
27
src/syn/lexer.l
Normal file
@@ -0,0 +1,27 @@
|
||||
%%
|
||||
[\n;]+ "EOL"
|
||||
[a-zA-Z_][a-zA-Z0-9_]* "IDENT"
|
||||
:[a-zA-Z_][a-zA-Z0-9_]* "SYM"
|
||||
[0-9]+ "NUM"
|
||||
"([^"]|\\[rnt"'\\])+"|'([^"]|\\[rnt"'\\])+' "STRING"
|
||||
|
||||
\|\| "||"
|
||||
&& "&&"
|
||||
< "<"
|
||||
> ">"
|
||||
<= "<="
|
||||
>= ">="
|
||||
!= "!="
|
||||
== "=="
|
||||
\+ "+"
|
||||
\* "*"
|
||||
/ "/"
|
||||
- "-"
|
||||
\( "("
|
||||
\) ")"
|
||||
\[ "["
|
||||
\] "]"
|
||||
\. "."
|
||||
, ","
|
||||
|
||||
[\t ]+ ;
|
||||
17
src/syn/mod.rs
Normal file
17
src/syn/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
pub mod ast;
|
||||
|
||||
pub mod lexer {
|
||||
lrlex_mod!("syn/lexer.l");
|
||||
use lrlex::lrlex_mod;
|
||||
pub use self::lexer_l::*;
|
||||
|
||||
}
|
||||
|
||||
pub mod parser {
|
||||
lrpar_mod!("syn/parser.y");
|
||||
use lrpar::lrpar_mod;
|
||||
pub use self::parser_y::*;
|
||||
}
|
||||
|
||||
//pub use lexer_l as lexer;
|
||||
//pub use parser_y as parser;
|
||||
144
src/syn/parser.y
Normal file
144
src/syn/parser.y
Normal file
@@ -0,0 +1,144 @@
|
||||
%start Body
|
||||
|
||||
%left '||'
|
||||
%left '&&'
|
||||
%left '<' '>' '<=' '>=' '==' '!='
|
||||
%left '*' '/'
|
||||
%left '+' '-'
|
||||
|
||||
%%
|
||||
|
||||
Body -> Result<Vec<Stmt>>:
|
||||
Body 'EOL' Stmt {
|
||||
flatten($1, $3)
|
||||
}
|
||||
| Stmt { Ok(vec![$1?]) }
|
||||
| { Ok(Vec::new()) }
|
||||
;
|
||||
|
||||
Stmt -> Result<Stmt>:
|
||||
Expr { Ok(Stmt::Expr($1?)) }
|
||||
;
|
||||
|
||||
Expr -> Result<Expr>: BinExpr { $1 };
|
||||
|
||||
BinExpr -> Result<Expr>:
|
||||
UnExpr '||' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Or, $3?)) }
|
||||
| UnExpr '&&' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::And, $3?)) }
|
||||
| UnExpr '<' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Lt, $3?)) }
|
||||
| UnExpr '>' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Gt, $3?)) }
|
||||
| UnExpr '<=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Le, $3?)) }
|
||||
| UnExpr '>=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Ge, $3?)) }
|
||||
| UnExpr '==' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Eq, $3?)) }
|
||||
| UnExpr '!=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Neq, $3?)) }
|
||||
| UnExpr '*' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Times, $3?)) }
|
||||
| UnExpr '/' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Div, $3?)) }
|
||||
| UnExpr '+' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) }
|
||||
| UnExpr '-' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) }
|
||||
| UnExpr { $1 }
|
||||
;
|
||||
|
||||
UnExpr -> Result<Expr>:
|
||||
'+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) }
|
||||
| '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) }
|
||||
| AccessExpr { $1 }
|
||||
;
|
||||
|
||||
AccessExpr -> Result<Expr>:
|
||||
AtomExpr AccessExprTail {
|
||||
Ok(AccessExpr::new_expr($1?, $2?))
|
||||
}
|
||||
;
|
||||
|
||||
AccessExprTail -> Result<Vec<Access>>:
|
||||
AccessExprTail '.' Ident ExprTrailing {
|
||||
flatten($1, Ok(Access { access: $3?, trailing: $4? }))
|
||||
}
|
||||
| '.' Ident ExprTrailing {
|
||||
Ok(vec![Access { access: $2?, trailing: $3? }])
|
||||
}
|
||||
;
|
||||
|
||||
ExprTrailing -> Result<Vec<ExprTrail>>:
|
||||
ExprTrailing '(' FunArgs ')' { flatten($1, Ok(ExprTrail::Call($3?))) }
|
||||
| ExprTrailing '[' Expr ']' { flatten($1, Ok(ExprTrail::Index($3?))) }
|
||||
| { Ok(Vec::new()) }
|
||||
;
|
||||
|
||||
FunArgs -> Result<Vec<Expr>>:
|
||||
FunArgsTail { $1 }
|
||||
| { Ok(Vec::new()) }
|
||||
;
|
||||
|
||||
FunArgsTail -> Result<Vec<Expr>>:
|
||||
FunArgsTail ',' Expr { flatten($1, $3) }
|
||||
| Expr { Ok(vec![$1?]) }
|
||||
;
|
||||
|
||||
AtomExpr -> Result<Expr>:
|
||||
Atom { $1.map(Expr::Atom) }
|
||||
| '(' Expr ')' { $2 }
|
||||
;
|
||||
|
||||
Atom -> Result<Atom>:
|
||||
Ident { Ok(Atom::Ident($1?)) }
|
||||
| 'SYM' {
|
||||
let v = $1.map_err(|_| ())?;
|
||||
let sym = &$lexer.span_str(v.span())[1..];
|
||||
Ok(Atom::Sym(sym.to_string()))
|
||||
}
|
||||
| 'NUM' {
|
||||
let v = $1.map_err(|_| ())?;
|
||||
let num_text = &$lexer.span_str(v.span());
|
||||
Ok(Atom::Num(num_text.parse().unwrap()))
|
||||
}
|
||||
| 'STRING' {
|
||||
let v = $1.map_err(|_| ())?;
|
||||
let string = &$lexer.span_str(v.span())[1..];
|
||||
Ok(Atom::String(parse_string(string)))
|
||||
}
|
||||
;
|
||||
|
||||
Ident -> Result<String>:
|
||||
'IDENT' {
|
||||
let v = $1.map_err(|_| ())?;
|
||||
let ident = $lexer.span_str(v.span()).to_string();
|
||||
Ok(ident)
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
use crate::syn::ast::*;
|
||||
|
||||
type Result<T> = std::result::Result<T, ()>;
|
||||
|
||||
fn flatten<T>(head: Result<Vec<T>>, tail: Result<T>) -> Result<Vec<T>> {
|
||||
let mut head = head?;
|
||||
let tail = tail?;
|
||||
head.push(tail);
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
fn parse_string(input: &str) -> String {
|
||||
let mut s = String::new();
|
||||
let input = &input[1..input.bytes().len() - 1];
|
||||
let mut chars = input.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '\\' {
|
||||
let next = chars.next().unwrap();
|
||||
let c = match next {
|
||||
'\\' => '\\',
|
||||
'\'' => '\'',
|
||||
'"' => '"',
|
||||
'n' => '\n',
|
||||
't' => '\t',
|
||||
_ => unreachable!(),
|
||||
};
|
||||
s.push(c);
|
||||
} else {
|
||||
s.push(c);
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
Reference in New Issue
Block a user