Files
not-python/src/syn/parser.y

189 lines
5.0 KiB
Plaintext
Raw Normal View History

%start Body
%left '||'
%left '&&'
%left '<' '>' '<=' '>=' '==' '!='
%left '*' '/'
%left '+' '-'
%avoid_insert "NUM" "SYM" "STRING" "||" "&&" "<" ">" "<=" ">=" "!=" "==" "+" "-" "*" "/" "{"
%%
Body -> Result<Vec<Stmt>>:
Body 'EOL' Stmt {
flatten($1, $3)
}
| Body 'EOL' { $1 }
| Stmt { Ok(vec![$1?]) }
| { Ok(Vec::new()) }
;
Stmt -> Result<Stmt>:
Expr { Ok(Stmt::Expr($1?)) }
| Assign { Ok(Stmt::Assign($1?)) }
| Return { Ok(Stmt::Return($1?)) }
;
Assign -> Result<AssignStmt>:
LhsExpr '=' Expr {
Ok(AssignStmt { lhs: $1?, rhs: $3? })
}
;
LhsExpr -> Result<LhsExpr>:
AccessExpr {
let lhs = match $1? {
Expr::Access(access) => LhsExpr::SetAttr(*access),
Expr::Atom(Atom::Ident(local)) => LhsExpr::Name(local),
_ => todo!("TODO : invalid syntax, raise error"),
};
Ok(lhs)
}
;
Return -> Result<ReturnStmt>:
'return' Expr { Ok(ReturnStmt { expr: Some($2?), }) }
| 'return' { Ok(ReturnStmt { expr: None, }) }
;
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::Minus, $3?)) }
| UnExpr { $1 }
;
UnExpr -> Result<Expr>:
'+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) }
| '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) }
| CallIndexExpr { $1 }
;
CallIndexExpr -> Result<Expr>:
IndexExpr { $1 }
| CallExpr { $1 }
| AccessExpr { $1 }
;
CallExpr -> Result<Expr>:
CallIndexExpr '(' FunArgs ')' {
Ok(CallExpr::new_expr($1?, $3?))
}
;
FunArgs -> Result<Vec<Expr>>:
FunArgsTail { $1 }
| { Ok(Vec::new()) }
;
FunArgsTail -> Result<Vec<Expr>>:
FunArgsTail ',' Expr { flatten($1, $3) }
| Expr { Ok(vec![$1?]) }
;
IndexExpr -> Result<Expr>:
CallIndexExpr '[' Expr ']' {
Ok(IndexExpr::new_expr($1?, $3?))
}
;
AccessExpr -> Result<Expr>:
FunExpr { $1 }
| CallIndexExpr '.' Ident { Ok(AccessExpr::new_expr($1?, $3?)) }
;
FunExpr -> Result<Expr>:
AtomExpr { $1 }
| 'fn' '(' FunParams ')' '{' Body '}' { Ok(FunExpr::new_expr($3?, $6?)) }
;
FunParams -> Result<Vec<String>>:
FunParamsTail { $1 }
| { Ok(Vec::new()) }
;
FunParamsTail -> Result<Vec<String>>:
FunParamsTail ',' Ident { flatten($1, $3) }
| Ident { 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[..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
}