443 lines
9.8 KiB
Plaintext
443 lines
9.8 KiB
Plaintext
use std::rc::Rc;
|
|
use crate::ast::*;
|
|
use crate::value::Value;
|
|
use crate::symbol::Symbol;
|
|
use crate::parser_util::*;
|
|
use crate::lstring::LStr;
|
|
use crate::lstr;
|
|
use num_complex::Complex64;
|
|
|
|
grammar;
|
|
|
|
extern {
|
|
type Error = ParseError;
|
|
|
|
}
|
|
|
|
match {
|
|
// line separator (use \ to escape newlines)
|
|
r";|\n" => LineSeparator,
|
|
|
|
// whitespace
|
|
r"[ \t\r]*" => {},
|
|
r"\\\r?\n" => {},
|
|
r"--[^\n]*" => {},
|
|
|
|
// kw literals
|
|
"true",
|
|
"false",
|
|
"nil",
|
|
// kw variables
|
|
"global",
|
|
"var",
|
|
// kw logic
|
|
"and",
|
|
"or",
|
|
"not",
|
|
// kw control flow
|
|
"begin",
|
|
"end",
|
|
"if",
|
|
"then",
|
|
"elif",
|
|
"else",
|
|
"while",
|
|
"for",
|
|
"do",
|
|
"in",
|
|
"continue",
|
|
"break",
|
|
"try",
|
|
"catch",
|
|
"return",
|
|
} else {
|
|
// identifiers
|
|
r"[a-zA-Z_][a-zA-Z0-9_]*" => TokIdentifier,
|
|
|
|
// literals
|
|
r"0x[0-9A-Fa-f][0-9A-Fa-f_]*" => TokHexInteger,
|
|
r"[0-9][0-9_]*" => TokDecInteger,
|
|
r"0o[0-7][0-7]*" => TokOctInteger,
|
|
r"0s[0-5][0-5]*" => TokSexInteger,
|
|
r"0b[01][01_]*" => TokBinInteger,
|
|
|
|
r"[0-9][0-9_]*([eE][-+]?[0-9_]*[0-9][0-9_]*i?|i)|([0-9][0-9_]*)?\.[0-9_]+([eE]_*[-+]?[0-9_]*[0-9][0-9_]*)?i?" => TokFloat,
|
|
|
|
r#""([^\\"]|\\.)*""# => TokStringDouble,
|
|
r#"'[^']*'"# => TokStringSingle,
|
|
|
|
r#":[a-zA-Z_][a-zA-Z0-9_]*"# => TokSymbolNone,
|
|
r#":'[^']*'"# => TokSymbolSingle,
|
|
r#":"([^\\"]|\\.)*""# => TokSymbolDouble,
|
|
} else {
|
|
// everything else
|
|
_
|
|
}
|
|
|
|
pub Block: Box<Expr<'input>> = {
|
|
LineSeparator* <xs:(<Expr> LineSeparator+)*> <x:Expr> LineSeparator* => {
|
|
let v = xs.into_iter().chain(std::iter::once(x)).map(|x| *x).collect();
|
|
return Box::new(Expr::Block(v));
|
|
},
|
|
LineSeparator* => Box::new(Expr::Block(Vec::new())),
|
|
}
|
|
|
|
|
|
Expr: Box<Expr<'input>> = {
|
|
"return" <e:Expr> => Box::new(Expr::Return(e)),
|
|
Assign,
|
|
}
|
|
|
|
//
|
|
// assignment
|
|
//
|
|
|
|
|
|
Assign: Box<Expr<'input>> = {
|
|
<l:LValue> <o:AssignOp> <r:Assign> => Box::new(Expr::Assign(o, l, r)),
|
|
"var" <i:Identifier> "=" <r:Assign> => Box::new(Expr::AssignVar(i, r)),
|
|
"global" <i:Identifier> "=" <r:Assign> => Box::new(Expr::AssignGlobal(i, r)),
|
|
Or,
|
|
}
|
|
|
|
LValue: Box<LValue<'input>> = {
|
|
<Identifier> => Box::new(LValue::Ident(<>)),
|
|
<l:BinaryIndex> "!" <r:CallOrAccess> => Box::new(LValue::Index(l, r)),
|
|
<l:CallOrAccess> "." <r:Identifier> => Box::new(LValue::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
|
|
}
|
|
|
|
AssignOp: Option<BinaryOp> = {
|
|
"=" => None,
|
|
"+=" => Some(BinaryOp::Add),
|
|
"-=" => Some(BinaryOp::Sub),
|
|
"*=" => Some(BinaryOp::Mul),
|
|
"/=" => Some(BinaryOp::Div),
|
|
"%=" => Some(BinaryOp::Mod),
|
|
"^=" => Some(BinaryOp::Pow),
|
|
"++=" => Some(BinaryOp::Concat),
|
|
"&=" => Some(BinaryOp::Append),
|
|
}
|
|
|
|
//
|
|
// logical ops
|
|
//
|
|
|
|
// or
|
|
Or: Box<Expr<'input>> = {
|
|
<l:Or> "or" <r:And> => Box::new(Expr::Or(l, r)),
|
|
And,
|
|
}
|
|
|
|
// and
|
|
And: Box<Expr<'input>> = {
|
|
<l:And> "and" <r:UnaryNot> => Box::new(Expr::And(l, r)),
|
|
UnaryNot,
|
|
}
|
|
|
|
// not
|
|
UnaryNot: Box<Expr<'input>> = {
|
|
"not" <r:Pipe> => Box::new(Expr::UnaryOp(UnaryOp::Not, r)),
|
|
Pipe,
|
|
}
|
|
|
|
//
|
|
// pipe
|
|
//
|
|
|
|
Pipe: Box<Expr<'input>> = {
|
|
<l:Pipe> "|" <r:Lambda> => Box::new(Expr::Pipe(l, r)),
|
|
Lambda,
|
|
}
|
|
|
|
//
|
|
// lambda
|
|
//
|
|
|
|
Lambda: Box<Expr<'input>> = {
|
|
"\\" <xs:IdentList> "->" <e:BinaryCompare> => Box::new(Expr::Lambda(xs, e)),
|
|
":" <e:BinaryCompare> => Box::new(Expr::Lambda(vec![lstr!("$")], e)),
|
|
"::" <e:BinaryCompare> => Box::new(Expr::Lambda(vec![lstr!("$"), lstr!("$$")], e)),
|
|
BinaryCompare,
|
|
}
|
|
|
|
//
|
|
// operations
|
|
//
|
|
|
|
// == != > < >= <=
|
|
BinaryCompare: Box<Expr<'input>> = {
|
|
<l:BinaryConcat> <o:CompareOp> <r:BinaryConcat> => Box::new(Expr::BinaryOp(o, l, r)),
|
|
BinaryConcat,
|
|
}
|
|
|
|
CompareOp: BinaryOp = {
|
|
"==" => BinaryOp::Eq,
|
|
"!=" => BinaryOp::Ne,
|
|
">" => BinaryOp::Gt,
|
|
"<" => BinaryOp::Lt,
|
|
">=" => BinaryOp::Ge,
|
|
"<=" => BinaryOp::Le,
|
|
}
|
|
|
|
|
|
// concat (++)
|
|
BinaryConcat: Box<Expr<'input>> = {
|
|
<l:BinaryConcat> "++" <r:BinaryAppend> => Box::new(Expr::BinaryOp(BinaryOp::Concat, l, r)),
|
|
BinaryAppend,
|
|
}
|
|
|
|
// append ( & )
|
|
BinaryAppend: Box<Expr<'input>> = {
|
|
<l:BinaryAppend> "&" <r:BinaryRange> => Box::new(Expr::BinaryOp(BinaryOp::Append, l, r)),
|
|
BinaryRange,
|
|
}
|
|
|
|
|
|
|
|
// .. ..= ..*
|
|
BinaryRange: Box<Expr<'input>> = {
|
|
<l:BinaryBitOr> <o:RangeOp> <r:BinaryBitOr> => Box::new(Expr::BinaryOp(o, l, r)),
|
|
<l:BinaryBitOr> "..*" => Box::new(Expr::UnaryOp(UnaryOp::RangeEndless, l)),
|
|
BinaryBitOr,
|
|
}
|
|
|
|
RangeOp: BinaryOp = {
|
|
".." => BinaryOp::Range,
|
|
"..=" => BinaryOp::RangeIncl,
|
|
}
|
|
// #|
|
|
BinaryBitOr: Box<Expr<'input>> = {
|
|
<l:BinaryBitOr> "#|" <r:BinaryBitXor> => Box::new(Expr::BinaryOp(BinaryOp::BitOr, l, r)),
|
|
BinaryBitXor,
|
|
}
|
|
|
|
// #^
|
|
BinaryBitXor: Box<Expr<'input>> = {
|
|
<l:BinaryBitXor> "#^" <r:BinaryBitAnd> => Box::new(Expr::BinaryOp(BinaryOp::BitXor, l, r)),
|
|
BinaryBitAnd,
|
|
}
|
|
|
|
// #&
|
|
BinaryBitAnd: Box<Expr<'input>> = {
|
|
<l:BinaryBitAnd> "#&" <r:BinaryShift> => Box::new(Expr::BinaryOp(BinaryOp::BitAnd, l, r)),
|
|
BinaryShift,
|
|
}
|
|
|
|
// >> <<
|
|
BinaryShift: Box<Expr<'input>> = {
|
|
<l:BinaryShift> <o:ShiftOp> <r:BinaryAdd> => Box::new(Expr::BinaryOp(o, l, r)),
|
|
BinaryAdd,
|
|
}
|
|
|
|
ShiftOp: BinaryOp = {
|
|
">>" => BinaryOp::Shr,
|
|
"<<" => BinaryOp::Shl,
|
|
}
|
|
|
|
// + -
|
|
BinaryAdd: Box<Expr<'input>> = {
|
|
<l:BinaryAdd> <o:AddOp> <r:BinaryMul> => Box::new(Expr::BinaryOp(o, l, r)),
|
|
BinaryMul,
|
|
}
|
|
|
|
AddOp: BinaryOp = {
|
|
"+" => BinaryOp::Add,
|
|
"-" => BinaryOp::Sub,
|
|
}
|
|
|
|
|
|
// * / %
|
|
BinaryMul: Box<Expr<'input>> = {
|
|
<l:BinaryMul> <o:MulOp> <r:UnaryMinus> => Box::new(Expr::BinaryOp(o, l, r)),
|
|
UnaryMinus,
|
|
}
|
|
|
|
MulOp: BinaryOp = {
|
|
"*" => BinaryOp::Mul,
|
|
"/" => BinaryOp::Div,
|
|
"//" => BinaryOp::IntDiv,
|
|
"%" => BinaryOp::Mod,
|
|
}
|
|
|
|
|
|
// unary-
|
|
UnaryMinus: Box<Expr<'input>> = {
|
|
"-" <r:BinaryPow> => Box::new(Expr::UnaryOp(UnaryOp::Neg, r)),
|
|
BinaryPow,
|
|
}
|
|
|
|
|
|
// power ( ^ )
|
|
BinaryPow: Box<Expr<'input>> = {
|
|
<l:BinaryIndex> "^" <r:UnaryMinus> => Box::new(Expr::BinaryOp(BinaryOp::Pow, l, r)),
|
|
BinaryIndex,
|
|
}
|
|
|
|
|
|
// index ( ! )
|
|
BinaryIndex: Box<Expr<'input>> = {
|
|
<l:BinaryIndex> "!" <r:UnaryMinus2> => Box::new(Expr::Index(l, r)),
|
|
CallOrAccess,
|
|
}
|
|
|
|
// unary-
|
|
UnaryMinus2: Box<Expr<'input>> = {
|
|
"-" <r:UnaryMinus2> => Box::new(Expr::UnaryOp(UnaryOp::Neg, r)),
|
|
CallOrAccess,
|
|
}
|
|
|
|
|
|
//
|
|
// things
|
|
//
|
|
|
|
// function call
|
|
CallOrAccess: Box<Expr<'input>> = {
|
|
<l:CallOrAccess> "(" <args:ExprList> ")" => Box::new(Expr::FnCall(l, args)),
|
|
<l:CallOrAccess> "->" <r:Identifier> "(" <args:ExprList> ")" => Box::new(Expr::AssocFnCall(l, Symbol::get(r), args)),
|
|
<l:CallOrAccess> "." <r:Identifier> => Box::new(Expr::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
|
|
Term,
|
|
}
|
|
|
|
//
|
|
// base
|
|
//
|
|
|
|
|
|
Term: Box<Expr<'input>> = {
|
|
Identifier => Box::new(Expr::Ident(<>)),
|
|
TermNotIdent,
|
|
}
|
|
|
|
TermNotIdent: Box<Expr<'input>> = {
|
|
"(" <Expr> ")" => <>,
|
|
"[" <ExprList> "]" => Box::new(Expr::List(<>)),
|
|
"{" <TableItems> "}" => Box::new(Expr::Table(<>)),
|
|
"$" => Box::new(Expr::Ident(lstr!("$"))),
|
|
"$$" => Box::new(Expr::Ident(lstr!("$$"))),
|
|
|
|
"do" <Block> "end" => <>,
|
|
"if" <IfStmtChain> => <>,
|
|
"while" <a:Expr> "do" <b:Block> "end"
|
|
=> Box::new(Expr::While(a, b)),
|
|
"for" <v:Identifier> "in" <a:Expr> "do" <b:Block> "end"
|
|
=> Box::new(Expr::For(v, a, b)),
|
|
"try" <b:Block> <mut ch:CatchChain> => {
|
|
ch.reverse();
|
|
Box::new(Expr::Try(b, ch))
|
|
},
|
|
|
|
Literal => Box::new(Expr::Literal(<>)),
|
|
}
|
|
|
|
IfStmtChain: Box<Expr<'input>> = {
|
|
<a:Expr> "then" <b:Block> "end" => Box::new(Expr::If(a, b, None)),
|
|
<a:Expr> "then" <b:Block> "else" <c:Block> "end" => Box::new(Expr::If(a, b, Some(c))),
|
|
<a:Expr> "then" <b:Block> "elif" <c:IfStmtChain> => Box::new(Expr::If(a, b, Some(c))),
|
|
}
|
|
|
|
CatchChain: Vec<CatchBlock<'input>> = {
|
|
"catch" <types:SymbolList> <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
|
|
|
|
ch.push(CatchBlock { name, types: Some(types), body: *b });
|
|
ch
|
|
},
|
|
"catch" "*" <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
|
|
ch.push(CatchBlock { name, types: None, body: *b });
|
|
ch
|
|
},
|
|
"end" => Vec::new(),
|
|
}
|
|
|
|
SymbolList: Vec<Symbol> = {
|
|
<mut xs:(<SymbolLiteral> ",")*> <x:SymbolLiteral?> => {
|
|
if let Some(x) = x { xs.push(x) };
|
|
xs
|
|
},
|
|
}
|
|
|
|
ExprList: Vec<Expr<'input>> = {
|
|
<xs:(<Expr> ",")*> <x:Expr?> => {
|
|
let mut xs: Vec<_> = xs.into_iter().map(|x| *x).collect();
|
|
if let Some(x) = x { xs.push(*x) };
|
|
xs
|
|
}
|
|
}
|
|
|
|
TableItems: Vec<(Expr<'input>, Expr<'input>)> = {
|
|
<mut xs:(<TableItem> ",")*> <x:TableItem?> => {
|
|
if let Some(x) = x { xs.push(x) };
|
|
xs
|
|
}
|
|
}
|
|
|
|
TableItem: (Expr<'input>, Expr<'input>) = {
|
|
<k:TableKey> "=" <v:Expr> => (k, *v),
|
|
}
|
|
|
|
TableKey: Expr<'input> = {
|
|
Identifier => Expr::Literal(Value::Symbol(Symbol::get(<>))),
|
|
TermNotIdent => *<>,
|
|
}
|
|
|
|
IdentList: Vec<&'input LStr> = {
|
|
<mut xs:(<Identifier> ",")*> <x:Identifier?>
|
|
=> { if let Some(x) = x { xs.push(x) }; xs }
|
|
}
|
|
|
|
Identifier: &'input LStr = TokIdentifier => <>.into();
|
|
|
|
|
|
//
|
|
// literals
|
|
//
|
|
|
|
Literal: Value = {
|
|
TokDecInteger =>? parse_int(<>, 10)
|
|
.map(Value::Int)
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
TokHexInteger =>? parse_int(&<>[2..], 16)
|
|
.map(Value::Int)
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
TokOctInteger =>? parse_int(&<>[2..], 8)
|
|
.map(Value::Int)
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
TokSexInteger =>? parse_int(&<>[2..], 6)
|
|
.map(Value::Int)
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
TokBinInteger =>? parse_int(&<>[2..], 2)
|
|
.map(Value::Int)
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
<f:TokFloat> =>? {
|
|
if let Some(f) = f.strip_suffix('i') {
|
|
parse_float(f)
|
|
.map(|im| Value::Complex(Complex64::new(0.0, im)))
|
|
.map_err(|e| ParseError::from(e).into())
|
|
} else {
|
|
parse_float(f)
|
|
.map(Value::Float)
|
|
.map_err(|e| ParseError::from(e).into())
|
|
}
|
|
},
|
|
StringLiteral => Value::String(<>),
|
|
SymbolLiteral => Value::Symbol(<>),
|
|
"true" => Value::Bool(true),
|
|
"false" => Value::Bool(false),
|
|
"nil" => Value::Nil,
|
|
}
|
|
|
|
StringLiteral: Rc<LStr> = {
|
|
TokStringSingle => LStr::from_str(&<>[1..<>.len()-1]).into(),
|
|
TokStringDouble =>? parse_str_escapes(&<>[1..<>.len()-1])
|
|
.map(|s| s.into())
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
}
|
|
|
|
SymbolLiteral: Symbol = {
|
|
TokSymbolNone => Symbol::get(&<>[1..]),
|
|
TokSymbolSingle => Symbol::get(&<>[2..<>.len()-1]),
|
|
TokSymbolDouble =>? parse_str_escapes(&<>[2..<>.len()-1])
|
|
.map(|s| Symbol::get(&s))
|
|
.map_err(|e| ParseError::from(e).into()),
|
|
}
|