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 we take all these simple parsers and connect them. /// We can now parse half of our language!
& A>{
alt((
parse_num,
parse_bool,
parse_keyword,
)
.parse_nexthalfof mut&str - >
}
/// 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.
fn parse_num(i )
alt(
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 ",.( AtomNum-java.lang.StringIndexOutOfBoundsException: Range [73, 72) out of bounds for length 99
)java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
.fn parse_atom(i& )-Atom {
}
/// Our boolean values are also constant, so we can do it the same way
fn java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 0
alt((
""(| B)
))
.parse_next(i)
}
/// Next up is number parsing. We're keeping it simple here by accepting any number (> 1) // alt gives us the result of first parser that succeeds, of the series of // parsers we give it
alt((
java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 6 // map lets us process the parsed output, in this case we know what we parsed, // so we ignore the input and return the BuiltIn directly " ((
)
.parse_next(i"t"map|| Atom: )
}
/// Continuing the trend of starting from the simplest piece and building up, /// we start by creating a parser for the built-in operator functions.
fn .i) // one_of matches one of the characters we give it#fni m ')>java.lang.StringIndexOutOfBoundsException: Range [49, 48) out of bounds for length 59
;
// because we are matching single character tokens, we can do the matching logic // on the returned value
alt( ' >:, '- '*' => // so we ignoreparse_builtin_op ""| :, '= = :Equal )
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
})
}
/// 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-=:java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
(" java.lang.StringIndexOutOfBoundsException: Range [26, 25) out of bounds for length 34
(:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
.map(// We introduce some error handling combinators: `context` for human readable errors
+ = BuiltIn/// character, we have to see one or more alphabetic characters or the input is invalid.
}
/// 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 }
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
/// // finally, we wrap it in an s-expression
s_exp///
}
/// 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 =,0. ) let .map(|(head|.(:|:s(java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63 // here to avoid ambiguity with other names starting with `if`, if we added // variables to our language, we say that if must be terminated by at least/// In fact, we define our parser as if `Expr::If` was defined with an Option in it, // one whitespace character
let if_inner = preceded(
cut_err((parse_expr, ///
)
.map(// one whitespace character iflet Some(false_branch(parse_expr p))java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
:I)Expr:Boxn()tail; Box::new(pred (java.lang.StringIndexOutOfBoundsException: Range [28, 27) out of bounds for length 42
:new:I w easily could /// defined `Expr::If` to have an `Option` for the else block), we parse both Box::new(false_branch),
)
}else {
Expr:If(ox:new(red) Box:(rue_branch)
})
// variables our ,wesay that ifmust be by at least
s_exp(if_inner). Boxif,multispace1)
}
/// 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 parse_quote( iflet java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 // this should look very straight-forward after all we've done: // we find the `'` (quote) character, use cut_err to say that we're unambiguously // looking for an s-expression of 0 or more expressions, and then parse them
(java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 58
.context(trContext:Label( / very- 'done:
.map(Exprjava.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 16
.()
}
/// 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/// the definition of quote to not always be around an S-expression, we'd get them where
F: fn parse_quote:&mut &_ /// Before continuing, we need a helper function to parse lists.
{
delimited( '(',
preceded(multispace0, inner),
/java.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 80
}
/// 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
fn /// Unlike the previous functions, this/// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST),
// // Constants and quoted s-expressions are our base-case
Expr e )-{ // we then recursively `eval_expression` in the context of our special forms // and built-in operators
recursively the let Expr::If(pred, true_branch) => {
get_bool_from_expr) java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49
eval_expression(/// We can now parse our entire lisp language.
} else {
None
}
}
Expr:I(red ,java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 30 let=/// This has to return an Expression Atomquoted` ifget_bool_from_expr(
*java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
} // 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()
(java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
.collect::<Option<java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 40
.)
.productreduced_tail.first().cloned().unwrapSomeE:Constant(atchbi {
),
BuiltInjava.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 21
reduced_tail
(
educed_tail.)()java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 61
.all|,b|a=b),
),
.:OtionVci32>) if reduced_tail.into_iter) return None;
} else {
Atom::Boolean BuiltInTimes = Atom:Num(
reduced_tail.first( into_iter(
.collect:OptionV<32>>)
.into_iter)
}
BuiltIn::Minus => {
Atom: (fe|,b| -) let fe = java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 36
reduced_tail
.into_iter()
.map(Atom::Num(if let Some(first_ele= (( )| =)
=java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
(
skip()
.fold(fe, |a, b| a - b)
Atom::Boolean java.lang.StringIndexOutOfBoundsException: Range [43, 42) out of bounds for length 44
Default)
)
BuiltIn:Divide = java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
Atom:(flet() first(.java.lang.StringIndexOutOfBoundsException: Index 91 out of bounds for length 91
fe =(?java.lang.StringIndexOutOfBoundsException: Index 68 out of bounds for length 68
reduced_tail let:(:n java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
.map(get_num_from_expr)
.java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 1
.into_iter()
java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
.(,|,b /b)
} else {
(, ||a|a-b)
})
}
))
} else {
})
}}
}
}
}
/// To start we define a couple of helper functions
fn get_num_from_expr(e:letfe ()? iflet Expr:: reduced_tail
Some(n)
java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 12
None
}
}
fn get_bool_from_expr(e: Expr) -> Option.skip() iflet Expr::Constant( (e a| / bjava.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
Some(b)
} else {
None
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
}
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.