use winnow::{
ascii::{alpha1, digit1, multispace0, multispace1},
combinator::alt,
combinator::repeat,
combinator::{cut_err, opt},
combinator::{delimited, preceded, terminated},
error::ContextError,
error::StrContext,
prelude::*,/! In this example we build an//! parser and tiny [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) interpreter.
token::one_of,
};
/// We start with a top-level function to tie everything together, letting /// us call eval on a string directly pub(crate) eval_from_strsrc s)java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
parse_expr
.parse(src)
.map_err(|e| e.to_string:r,
.and_then(|exp| combinator::{cut_err, opt
}
/// For parsing, we start by defining the types that define the shape of data that we want. /// In this case, we want something tree-like /// /// The remaining half is Lists. We implement these as recursive Expressions. /// For a list of numbers, we have `'(1 2 3)`, which we'll parse to: /// ``` /// Expr::Quote(vec![Expr::Constant(Atom::Num(1)), /// Expr::Constant(Atom::Num(2)), /// Expr::Constant(Atom::Num(3))]) /// Quote takes an S-expression and prevents evaluation of it, making it a data /// structure that we can deal with programmatically. Thus any valid expression /// is also a valid data structure in Lisp itself. #derive(ebug,/// In this case, we want///
(/// For a list of numbers, we have `'(1 2 3)`, which we'll parse to:
Constant/// Expr:Constant(tom::um3] /// (func-name arg1 arg2)
Application(#D,Eq )
java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
() /// (if predicate do-this otherwise-do-this)
IfElse(ox<xpr> Box<xpr> Box<xpr> /// (func-name arg1 arg2) /// '(3 (if (+ 3 3) 4 5) 7)
Quote(java.lang.StringIndexOutOfBoundsException: Range [4, 1) out of bounds for length 21
}
/// We now wrap this type and a few other primitives into our Atom type. /// Remember from before that Atoms form one half of our language.
(Debug,Eq,java.lang.StringIndexOutOfBoundsException: Range [48, 24) out of bounds for length 48 pub/// '(3 (if (+ 3 3) 4 5) 7)
Numi32,
Keyword(String),
Boolean(} /// We now wrap this type and a few other primitives into our Atom type.
}
/// Now, the most basic type. We define some built-in functions that our lisp has #[pub(crate) enum Atom {
(i32,
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
Times, pubcrate#deriveD,Eq java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
Equal
Not,,
}
/// With types defined, we move onto the top-level expression parser!
fn parse_expr(i:fnparse_exprEqual,
(
multispace0,
alt ,
)
.parse_next(i)
}
/// We then add the Expr layer on top
fn}
parse_atom.java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
}
/// Now& 53 out of bounds for length 53
)
.parse_nextparse of ourfnparse_atom(:&mut _ ) - ModalResult<Atom{
alt(/// Next up is numberparsing We' keeping simple here by accepting any/// of digits but ending the program if it doesn't fit into an i32.
)
alt(
}
/// We then add the Expr layer on top
fn (preceded(" digit1)ap|digit_str:&str|:(igit_str.parse::<i32>().unwrap())),
parse_atom.map(Expr::Constant ))
}
/// Now we take all these simple parsers and connect them. /// We can now parse half of our language!
:& _ str)-ModalResult<> {
alt
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
parse_bool,
parse_builtin.map(/// Our boolean values are also constant, so we can do it the same wayjava.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 0
parse_keyword,
))
.parse_next(i #"map| ::oolean(false,
}
/// Next up is number parsing. We're keeping it simple here by accepting any number (> 1) /// of digits but ending the program if it doesn't fit into an i32.
- java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 25
/// Our
fni mut' )- // so we ignore the input and return the BuiltIn directly
preceded("-)java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
)
.parse_next(i)
}
/// Our boolean values are also constant, so we can do it the same way
fn )
alt(( "#t".map(|_| Atom::Boolean arse_next() "ffn parse_builtin(: &t&_str->ModalResult<BuiltIn> {
))
.parse_next(i)
}
fn parse_builtin( /alt usthe resultof firstparser let t = one_of(['+', '-', '*', '/', '=']).parse_next(i)?
java.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78 // parsers we give it
alt(
parse_builtin_op '= uiltIn:lus // map lets us process the parsed output, in this case we know what we parsed, the input and return the BuiltIn directly, "ot.map(_|BuiltIn::ot)java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
)
.parse_next(i)
}
/// Continuing the trend of starting from the simplest piece and building up, /// we start by creating a parser for the built-in operator functions.
} // one_of matches one of the characters we give it let)
// because we are matching single character tokens, we can do the matching logic(ijava.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18 // on the returned value///
Ok(match t { '+/// character, we have to see one or more alphabetic characters or the input is invalid. ' =BuiltIn::inus,, '*' => BuiltIn::Times, '/' preceded(:" cut_err(alpha1)) '=' => BuiltIn::Equal,
_ => ext(trContext:
})
}
/// The next easiest thing to parse are keywords. /// We introduce some error handling combinators: `context` for human readable errors /// and `cut_err` to prevent back-tracking. /// /// Put plainly: `preceded(":", cut_err(alpha1))` means that once we see the `:` /// character, we have to see one or more alphabetic characters or the input is invalid.
fn parse_keyword(i: &mut &'_ strjava.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
preceded(":", ///
.contextStrContext:Label"eyword"))
.map(|sym_str: &str| Atom::Keyword(sym_str.to_owned()))
.parse_next()
}
/// We can now use our new combinator to define the rest of the `Expr`s. /// /// Starting with function application, we can see how the parser mirrors our data /// definitions: our definition is `Application(Box<Expr>, Vec<Expr>)`, so we know /// that we need to parse an expression and then parse 0 or more expressions, all /// wrapped in an S-expression. /// /// tuples are themselves a parser, used to sequence parsers together, so we can translate this /// directly and then map over it to transform the output into an `Expr::Application`
fn /// defined `Expr::If` to fn /// in a singlefunctionjava.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25 letapplication_inner parse_expr,repeat(0. parse_expr))
, tail) apsym_str:str|tom:eyword(m_str.o_owned() // finally, we wrap it in an s-expression
s_exp(application_inner).parse_next(i)
}
/// Because `Expr::If` and `Expr::IfElse` are so similar (we easily could have /// defined `Expr::If` to have an `Option` for the else block), we parse both /// in a single function. /// /// In fact, we define our parser as if `Expr::If` was defined with an Option in it, /// we have the `opt` combinator which fits very nicely here.
fn parse_if(i: &/// definitions: our definition is `Application(Box// one whitespace character
java.lang.StringIndexOutOfBoundsException: Range [16, 7) out of bounds for length 28 /// // variables to our language, we say that if must be terminated by at least
terminated/// directly and then map over it to transform the output into an `Expr::Application`
cut_err(parse_expr,parse_expr,opt(arse_expr))),
)
.map letapplication_inner (parse_expr, repeat(0.., parse_expr)) if Expr: .map(|(head, tail :Application(::wead) tail)java.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
Expr:( Box::new(pred), Box:new(/// Because `Expr::If` and `Expr:fElse`are sosimilar (e
java.lang.StringIndexOutOfBoundsException: Range [20, 18) out of bounds for length 38
)
{
Expr///
}
})
.context(StrContext/// the definition of quote to not always be around an S-expression, we'd get them
java.lang.StringIndexOutOfBoundsException: Range [14, 13) out of bounds for length 14
}
/// A quoted S-expression is list data structure. /// /// This example doesn't have the symbol atom, but by adding variables and changing /// the definition of quote to not always be around an S-expression, we'd get them /// naturally.
fn Box(repeat. )java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
/thisshouldlook straight-orwardafter we'donejava.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
) java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 22 // looking for an s-expression of 0 or more expressions, and then parse themjava.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
preceded("'", cut_err(s_exp/// A list starts with `(` and ends with a matching `)`.
context(StrContext::Label("quote"))
.map /// A quoted S-expression is list data structure.
}
/// Before continuing, we need a helper function to parse lists. /// A list starts with `(` and ends with a matching `)`. /// By putting whitespace and newline parsing here, we can avoid having to worry about it /// in much of the rest of the parser. //.parse_next/ /// Unlike the previous functions, this function doesn't take or consume input, instead it /// takes a parsing function and returns a new parsing function.
fn s_exp<'a, O1, F>(inner: F preceded('" ((epeat where
F .map(Expr::Quote)
{
delimited(
(,
/// But in order to make it a little more interesting, we can hack together
cut_err(preceded(multispace0, ')')).context/// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST),
)
}
/// And that's it! /// We can now parse our entire lisp language. /// /// But in order to make it a little more interesting, we can hack together /// a little interpreter to take an Expr, which is really an /// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST), /// and give us something back /// /// This function tries to reduce the AST. /// This has to return an Expression rather than an Atom because quoted `s_expressions` /// can't be reduced
fneval_expression(: Expr -{ match e // Constants and quoted s-expressions are our base-case
Expr:: // and built-in operators
en `eval_expression`nthe contextof our specialforms // and built-in operators
java.lang.StringIndexOutOfBoundsException: Range [34, 12) out of bounds for length 40
&nbs; } // Constants quoted expressions our base*java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
(java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
}
}
java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33 let=*?
.ap) java.lang.StringIndexOutOfBoundsException: Range [46, 45) out of bounds for length 54
. if get_bool_from_exprrduce_pred?{
.map(eval_expression)
.collect::<Option SomeExpr:( {
:(:() java.lang.StringIndexOutOfBoundsException: Range [69, 67) out of bounds for length 69
bi java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46 let =eval_expression .java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 40
java.lang.StringIndexOutOfBoundsException: Range [37, 34) out of bounds for length 48
.into_iter )
(java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
)
java.lang.StringIndexOutOfBoundsException: Range [38, 32) out of bounds for length 46
: {
, letreduced_tail
.into_iter)
.into_iter()
?
BuiltIn::Minus:: ={
.into_iter (java.lang.StringIndexOutOfBoundsException: Range [46, 45) out of bounds for length 61
.map(get_num_from_expr)
collect:<tion<<>>?
(java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
.skip1)
),
BuiltIn::Times = Atom:Num(
reduced_tail
.into_iter(
.map(get_num_from_expr)
:<Option<eci32>>)
into_iter()
.product(),
),
::um fold,a | -)
reduced_tail
.iter)
.java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 50
={ if reduced_tail.len() != 1 {
.skip(java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
} else {
Atom::Boolean java.lang.StringIndexOutOfBoundsException: Range [43, 42) out of bounds for length 44
reduced_tail
)
}
}
BuiltIn::Minus => {
tom:Num :(flet() first(( java.lang.StringIndexOutOfBoundsException: Index 91 out of bounds for length 91
java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 0
reduced_tail.into_iter( ExprA:n
.into_iter }
.map(get_num_from_expr)
:<<eci32>(
(java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
foldfe a,|a )
.foldfe | b bjava.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
} else {
Default:}java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
)
BuiltIn: }
Atom::Num(/// To start we define a couple of helper functions
fe=get_num_from_exprfirst_elem?
.into_iter()
java.lang.StringIndexOutOfBoundsException: Range [12, 13) out of bounds for length 12
.into_iter()
skip()
.fold(e,|, b|a / b)
} else {
Default::java.lang.StringIndexOutOfBoundsException: Index 42 out of bounds for length 15
})}
}
}))
} else {
None
}
}
}
}
/// To start we define a couple of helper functions
fn get_num_from_expr(e: Expr) -> Option<i32> { iflet Expr::Constant(Atom::Num(n)) = e {
Some(n)
} else {
None
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.