mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-25 18:49:50 -04:00
Cleanup and formatting.
* Unused code warnings. * Ran cargo fmt.
This commit is contained in:
parent
1e063fd129
commit
a2f689ce0d
18
src/build.rs
18
src/build.rs
@ -661,7 +661,8 @@ impl Builder {
|
|||||||
v.insert((count, expr_result));
|
v.insert((count, expr_result));
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
Entry::Occupied(mut v) => { // overriding field here.
|
Entry::Occupied(mut v) => {
|
||||||
|
// overriding field here.
|
||||||
// Ensure that the new type matches the old type.
|
// Ensure that the new type matches the old type.
|
||||||
let src_val = v.get().clone();
|
let src_val = v.get().clone();
|
||||||
if src_val.1.type_equal(&expr_result) {
|
if src_val.1.type_equal(&expr_result) {
|
||||||
@ -681,12 +682,17 @@ impl Builder {
|
|||||||
// We want to maintain our order for the fields to make comparing tuples
|
// We want to maintain our order for the fields to make comparing tuples
|
||||||
// easier in later code. So we sort by the field order before constructing a new tuple.
|
// easier in later code. So we sort by the field order before constructing a new tuple.
|
||||||
new_fields.sort_by(|a, b| {
|
new_fields.sort_by(|a, b| {
|
||||||
let ta = a.1.clone(); let tb = b.1.clone(); ta.0.cmp(&tb.0)
|
let ta = a.1.clone();
|
||||||
|
let tb = b.1.clone();
|
||||||
|
ta.0.cmp(&tb.0)
|
||||||
});
|
});
|
||||||
return Ok(Rc::new(Val::Tuple(new_fields.iter().map(|a| {
|
return Ok(Rc::new(Val::Tuple(new_fields.iter()
|
||||||
let first = a.0.clone(); let t = a.1.clone();
|
.map(|a| {
|
||||||
(first, t.1)
|
let first = a.0.clone();
|
||||||
}).collect())));
|
let t = a.1.clone();
|
||||||
|
(first, t.1)
|
||||||
|
})
|
||||||
|
.collect())));
|
||||||
}
|
}
|
||||||
Err(Box::new(error::Error::new(format!("Expected Tuple got {}", v),
|
Err(Box::new(error::Error::new(format!("Expected Tuple got {}", v),
|
||||||
error::ErrorType::TypeFail,
|
error::ErrorType::TypeFail,
|
||||||
|
@ -33,7 +33,9 @@ impl JsonConverter {
|
|||||||
Ok(serde_json::Value::Array(v))
|
Ok(serde_json::Value::Array(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_tuple(&self, items: &Vec<(ast::Positioned<String>, Rc<Val>)>) -> Result<serde_json::Value> {
|
fn convert_tuple(&self,
|
||||||
|
items: &Vec<(ast::Positioned<String>, Rc<Val>)>)
|
||||||
|
-> Result<serde_json::Value> {
|
||||||
let mut mp = serde_json::Map::new();
|
let mut mp = serde_json::Map::new();
|
||||||
for &(ref k, ref v) in items.iter() {
|
for &(ref k, ref v) in items.iter() {
|
||||||
mp.entry(k.val.clone()).or_insert(try!(self.convert_value(v)));
|
mp.entry(k.val.clone()).or_insert(try!(self.convert_value(v)));
|
||||||
@ -50,7 +52,7 @@ impl JsonConverter {
|
|||||||
None => panic!("Float is too large or Not a Number {}", f),
|
None => panic!("Float is too large or Not a Number {}", f),
|
||||||
};
|
};
|
||||||
serde_json::Value::Number(n)
|
serde_json::Value::Number(n)
|
||||||
},
|
}
|
||||||
&Val::Int(i) => {
|
&Val::Int(i) => {
|
||||||
let n = match serde_json::Number::from_f64(i as f64) {
|
let n = match serde_json::Number::from_f64(i as f64) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
@ -58,20 +60,20 @@ impl JsonConverter {
|
|||||||
None => panic!("Float is too large or Not a Number {}", i),
|
None => panic!("Float is too large or Not a Number {}", i),
|
||||||
};
|
};
|
||||||
serde_json::Value::Number(n)
|
serde_json::Value::Number(n)
|
||||||
},
|
}
|
||||||
&Val::String(ref s) => serde_json::Value::String(s.clone()),
|
&Val::String(ref s) => serde_json::Value::String(s.clone()),
|
||||||
&Val::Macro(_) => {
|
&Val::Macro(_) => {
|
||||||
// TODO(jwall): We probably want to actually skip this but for now
|
// TODO(jwall): We probably want to actually skip this but for now
|
||||||
// we'll use null
|
// we'll use null
|
||||||
eprintln!("Skipping macro encoding as null...");
|
eprintln!("Skipping macro encoding as null...");
|
||||||
serde_json::Value::Null
|
serde_json::Value::Null
|
||||||
},
|
}
|
||||||
&Val::List(ref l) => try!(self.convert_list(l)),
|
&Val::List(ref l) => try!(self.convert_list(l)),
|
||||||
&Val::Tuple(ref t) => try!(self.convert_tuple(t)),
|
&Val::Tuple(ref t) => try!(self.convert_tuple(t)),
|
||||||
};
|
};
|
||||||
Ok(jsn_val)
|
Ok(jsn_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, v: &Val, w: &mut Write) -> Result<()> {
|
fn write(&self, v: &Val, w: &mut Write) -> Result<()> {
|
||||||
let jsn_val = try!(self.convert_value(v));
|
let jsn_val = try!(self.convert_value(v));
|
||||||
try!(serde_json::to_writer(w, &jsn_val));
|
try!(serde_json::to_writer(w, &jsn_val));
|
||||||
@ -83,4 +85,4 @@ impl Converter for JsonConverter {
|
|||||||
fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> Result<()> {
|
fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> Result<()> {
|
||||||
self.write(&v, &mut w)
|
self.write(&v, &mut w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
108
src/lib.rs
108
src/lib.rs
@ -14,22 +14,22 @@
|
|||||||
// #![feature(trace_macros,log_syntax)]
|
// #![feature(trace_macros,log_syntax)]
|
||||||
|
|
||||||
//! # ucg, A universal configuration grammar.
|
//! # ucg, A universal configuration grammar.
|
||||||
//!
|
//!
|
||||||
//! Ucg defines a common grammar for describing a collection of configuration values.
|
//! Ucg defines a common grammar for describing a collection of configuration values.
|
||||||
//! ucg allows you to specify configuration values with a syntax that that is immutable,
|
//! ucg allows you to specify configuration values with a syntax that that is immutable,
|
||||||
//! comoposable with copy-on-write semantics, and safe.
|
//! comoposable with copy-on-write semantics, and safe.
|
||||||
//!
|
//!
|
||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! // named bindings
|
//! // named bindings
|
||||||
//! let host = "mysql.internal.net";
|
//! let host = "mysql.internal.net";
|
||||||
//! let port = 8080
|
//! let port = 8080
|
||||||
//!
|
//!
|
||||||
//! // format strings
|
//! // format strings
|
||||||
//! let connstr = "mysql://@:@" % (host, port);
|
//! let connstr = "mysql://@:@" % (host, port);
|
||||||
//!
|
//!
|
||||||
//! // tuples
|
//! // tuples
|
||||||
//! let dbconf = {
|
//! let dbconf = {
|
||||||
//! connstr = connstr,
|
//! connstr = connstr,
|
||||||
//! database = "mydb",
|
//! database = "mydb",
|
||||||
@ -37,17 +37,17 @@
|
|||||||
//! tables = ["posts", "comments", "users"],
|
//! tables = ["posts", "comments", "users"],
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Syntax
|
//! ## Syntax
|
||||||
//!
|
//!
|
||||||
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
|
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
|
||||||
//! A valid ucg file is composesed of a series of statements. Statements are any valid
|
//! A valid ucg file is composesed of a series of statements. Statements are any valid
|
||||||
//! ucg expression terminated by a semicolon.
|
//! ucg expression terminated by a semicolon.
|
||||||
//!
|
//!
|
||||||
//! ### Reserved words
|
//! ### Reserved words
|
||||||
//!
|
//!
|
||||||
//! The following words are reserved in ucg and can't be used as named bindings.
|
//! The following words are reserved in ucg and can't be used as named bindings.
|
||||||
//!
|
//!
|
||||||
//! * let
|
//! * let
|
||||||
//! * import
|
//! * import
|
||||||
//! * as
|
//! * as
|
||||||
@ -55,50 +55,50 @@
|
|||||||
//! * macro
|
//! * macro
|
||||||
//!
|
//!
|
||||||
//! ### Primitive types
|
//! ### Primitive types
|
||||||
//!
|
//!
|
||||||
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
|
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
|
||||||
//!
|
//!
|
||||||
//! * Int, is any integer number.
|
//! * Int, is any integer number.
|
||||||
//! * Float is any number with a decimal point.
|
//! * Float is any number with a decimal point.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! 1.0; // A typical float.
|
//! 1.0; // A typical float.
|
||||||
//! 1. // You can leave off the 0 after the decimal point.
|
//! 1. // You can leave off the 0 after the decimal point.
|
||||||
//! .1 // the leading 0 is also optional.
|
//! .1 // the leading 0 is also optional.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * String is any quoted text. backslashes within a string escape the next preceding
|
//! * String is any quoted text. backslashes within a string escape the next preceding
|
||||||
//! character.
|
//! character.
|
||||||
//!
|
//!
|
||||||
//! ``` ucg
|
//! ``` ucg
|
||||||
//! "foo"; // a smiple string
|
//! "foo"; // a smiple string
|
||||||
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Complex types
|
//! ### Complex types
|
||||||
//!
|
//!
|
||||||
//! ucg has two complex data types, Lists and Tuples.
|
//! ucg has two complex data types, Lists and Tuples.
|
||||||
//!
|
//!
|
||||||
//! * List's start are surrounded with square brackets `[ ]` and have comma separated elements.
|
//! * List's start are surrounded with square brackets `[ ]` and have comma separated elements.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! [1, 2, 3]; // A simple list of numbers.
|
//! [1, 2, 3]; // A simple list of numbers.
|
||||||
//!
|
//!
|
||||||
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
|
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
|
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let mylist = [0, 1, 2, 3];
|
//! let mylist = [0, 1, 2, 3];
|
||||||
//!
|
//!
|
||||||
//! let zero = mylist.0;
|
//! let zero = mylist.0;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
|
//! * Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
|
||||||
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
|
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
|
||||||
//! be a bareword without quotes.
|
//! be a bareword without quotes.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let mytuple = {
|
//! let mytuple = {
|
||||||
//! field1 = "value1",
|
//! field1 = "value1",
|
||||||
@ -111,17 +111,17 @@
|
|||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let field = mytuple.fields1;
|
//! let field = mytuple.fields1;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Expressions
|
//! ### Expressions
|
||||||
//!
|
//!
|
||||||
//! #### Selectors
|
//! #### Selectors
|
||||||
//!
|
//!
|
||||||
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
|
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
|
||||||
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
|
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
|
||||||
//! followed by either a bareword to index a tuple field or an integer to index a list position.
|
//! followed by either a bareword to index a tuple field or an integer to index a list position.
|
||||||
//!
|
//!
|
||||||
//! The simplest selector is just a reference to a bound value by name.
|
//! The simplest selector is just a reference to a bound value by name.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let mytuple = {
|
//! let mytuple = {
|
||||||
//! field1 = "a string",
|
//! field1 = "a string",
|
||||||
@ -129,7 +129,7 @@
|
|||||||
//! subfield1 = 1,
|
//! subfield1 = 1,
|
||||||
//! }];
|
//! }];
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
|
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
@ -138,59 +138,59 @@
|
|||||||
//! ucg supports the following operators, +, -, *, /; Each one is type safe and infers the types from the values they operate on.
|
//! ucg supports the following operators, +, -, *, /; Each one is type safe and infers the types from the values they operate on.
|
||||||
//! The operators expect both the left and right operands to be of the same type. All of the operators are valid on integers and floats.
|
//! The operators expect both the left and right operands to be of the same type. All of the operators are valid on integers and floats.
|
||||||
//! The + operator can additionally concatenate strings or arrays.
|
//! The + operator can additionally concatenate strings or arrays.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! 1 + 1; // result is 2
|
//! 1 + 1; // result is 2
|
||||||
//! "foo " + "bar" // result is "foo bar"
|
//! "foo " + "bar" // result is "foo bar"
|
||||||
//! [1,2] + [3,4]; // result is [1,2,3,4]
|
//! [1,2] + [3,4]; // result is [1,2,3,4]
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! #### Conditional data.
|
//! #### Conditional data.
|
||||||
//!
|
//!
|
||||||
//! ucg supports a limited form of conditional selection of data using the select expression. A select expression starts with the select
|
//! ucg supports a limited form of conditional selection of data using the select expression. A select expression starts with the select
|
||||||
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
|
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
|
||||||
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
|
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let want = "baz";
|
//! let want = "baz";
|
||||||
//!
|
//!
|
||||||
//! // field default
|
//! // field default
|
||||||
//! select want, "quux", {
|
//! select want, "quux", {
|
||||||
//! baz = "foo",
|
//! baz = "foo",
|
||||||
//! fuzz = "bang",
|
//! fuzz = "bang",
|
||||||
//! }; // result will be "foo"
|
//! }; // result will be "foo"
|
||||||
//!
|
//!
|
||||||
//! // field default
|
//! // field default
|
||||||
//! select "quack", "quux", {
|
//! select "quack", "quux", {
|
||||||
//! baz = "foo",
|
//! baz = "foo",
|
||||||
//! fuzz = "bang",
|
//! fuzz = "bang",
|
||||||
//! }; // result will be "quux"
|
//! }; // result will be "quux"
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! #### Macros
|
//! #### Macros
|
||||||
//!
|
//!
|
||||||
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
|
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
|
||||||
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the macro
|
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the macro
|
||||||
//! keyword followed by the arguments in parentheses and then a tuple.
|
//! keyword followed by the arguments in parentheses and then a tuple.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let myfunc = macro (arg1, arg2) {
|
//! let myfunc = macro (arg1, arg2) {
|
||||||
//! host = arg1,
|
//! host = arg1,
|
||||||
//! port = arg2,
|
//! port = arg2,
|
||||||
//! connstr = "couchdb://@:@" % (arg1, arg2),
|
//! connstr = "couchdb://@:@" % (arg1, arg2),
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! let my dbconf = myfunc("couchdb.example.org", "9090");
|
//! let my dbconf = myfunc("couchdb.example.org", "9090");
|
||||||
//!
|
//!
|
||||||
//! let my dbhost = dbconf.host;
|
//! let my dbhost = dbconf.host;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
|
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
|
||||||
//!
|
//!
|
||||||
//! ### Statements
|
//! ### Statements
|
||||||
//!
|
//!
|
||||||
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
|
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
|
||||||
//!
|
//!
|
||||||
//! * expression statements
|
//! * expression statements
|
||||||
//! The simplest and least useful is the expression statements. It is any valid expression followed by a semicolon.
|
//! The simplest and least useful is the expression statements. It is any valid expression followed by a semicolon.
|
||||||
//!
|
//!
|
||||||
@ -199,27 +199,27 @@
|
|||||||
//! 4 / 2;
|
//! 4 / 2;
|
||||||
//! "foo";
|
//! "foo";
|
||||||
//! "foo" + "bar";
|
//! "foo" + "bar";
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
|
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
|
||||||
//! ever create a repl for ucg statements they may prove more useful.
|
//! ever create a repl for ucg statements they may prove more useful.
|
||||||
//!
|
//!
|
||||||
//! * Let statements
|
//! * Let statements
|
||||||
//! The let expression binds the result of any valid expression to a name. It starts with the let keyword and is followed by
|
//! The let expression binds the result of any valid expression to a name. It starts with the let keyword and is followed by
|
||||||
//! the name of the binding, =, and a valid ucg expression.
|
//! the name of the binding, =, and a valid ucg expression.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let name = "foo";
|
//! let name = "foo";
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * Import statement
|
//! * Import statement
|
||||||
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
|
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
|
||||||
//! values are exposed as a tuple in the referencing file. It starts with the import keyword and is followed by a quoted path
|
//! values are exposed as a tuple in the referencing file. It starts with the import keyword and is followed by a quoted path
|
||||||
//! to the ucg file, they keyword as, and a name for the imported values.
|
//! to the ucg file, they keyword as, and a name for the imported values.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! import "dbconfigs.ucg" as dbconfigs;
|
//! import "dbconfigs.ucg" as dbconfigs;
|
||||||
//!
|
//!
|
||||||
//! let mysqlconf = dbconfigs.mysql;
|
//! let mysqlconf = dbconfigs.mysql;
|
||||||
//! ```
|
//! ```
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
18
src/parse.rs
18
src/parse.rs
@ -677,24 +677,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_incomplete {
|
|
||||||
($parsemac:ident( $i:expr )) => {
|
|
||||||
assert_incomplete!($i, $parsemac)
|
|
||||||
};
|
|
||||||
($i:expr, $f:expr) => {
|
|
||||||
{
|
|
||||||
let input = LocatedSpan::new($i);
|
|
||||||
match tokenize(input) {
|
|
||||||
Err(_) => assert!(false),
|
|
||||||
Ok(val) => {
|
|
||||||
let result = $f(TokenIter{source: val.as_slice()});
|
|
||||||
assert!(result.is_incomplete(), format!("Not Incomplete: {:?}", result));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_error {
|
macro_rules! assert_error {
|
||||||
($parsemac:ident( $i:expr )) => {
|
($parsemac:ident( $i:expr )) => {
|
||||||
assert_error!($i, $parsemac)
|
assert_error!($i, $parsemac)
|
||||||
|
@ -40,9 +40,11 @@ fn escapequoted(input: Span) -> nom::IResult<Span, String> {
|
|||||||
let mut frag = String::new();
|
let mut frag = String::new();
|
||||||
let mut escape = false;
|
let mut escape = false;
|
||||||
for (i, c) in input.iter_indices() {
|
for (i, c) in input.iter_indices() {
|
||||||
if c == '\\' && ! escape { // eat this slash and set our escaping sentinel
|
if c == '\\' && !escape {
|
||||||
|
// eat this slash and set our escaping sentinel
|
||||||
escape = true;
|
escape = true;
|
||||||
} else if c == '"' && !escape { // Bail if this is an unescaped "
|
} else if c == '"' && !escape {
|
||||||
|
// Bail if this is an unescaped "
|
||||||
// we exit here.
|
// we exit here.
|
||||||
return nom::IResult::Done(input.slice(i..), frag);
|
return nom::IResult::Done(input.slice(i..), frag);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user