Compare commits
4 Commits
67ea72a990
...
ce2636f018
| Author | SHA1 | Date | |
|---|---|---|---|
| ce2636f018 | |||
| ae6ad84b5c | |||
| 6984455c12 | |||
| c950b70c9b |
@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rune"
|
name = "rune"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
description = "A lexical analysis library."
|
description = "A lexical analysis library."
|
||||||
repository = "/myrddin/rune"
|
repository = "https://workshop.cybermages.tech/CyberMages/rune"
|
||||||
authors = ["CyberMages LLC <Software@CyberMagesLLC.com>", "Jason Travis Smith <Myrddin@CyberMages.tech>"]
|
authors = ["CyberMages LLC <Software@CyberMagesLLC.com>", "Jason Travis Smith <Myrddin@CyberMages.tech>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license-file = "LICENSE.md"
|
license-file = "LICENSE.md"
|
||||||
|
|||||||
219
LICENSE.md
219
LICENSE.md
@ -1,69 +1,174 @@
|
|||||||
Copyright (c) <2025> CyberMages LLC
|
# Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
<http://www.apache.org/licenses/>
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or
|
## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
without modification, are permitted provided that the
|
|
||||||
following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above
|
### 1. Definitions.
|
||||||
copyright notice, this list of conditions and the
|
|
||||||
following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above
|
**"License"** shall mean the terms and conditions for use, reproduction,
|
||||||
copyright notice, this list of conditions and the
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
following disclaimer in the documentation and/or other
|
|
||||||
materials provided with the distribution.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this license, each
|
**"Licensor"** shall mean the copyright owner or entity authorized by
|
||||||
copyright holder and contributor hereby grants to those
|
the copyright owner that is granting the License.
|
||||||
receiving rights under this license a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable (except for failure to satisfy the conditions
|
|
||||||
of this license) patent license to make, have made, use,
|
|
||||||
offer to sell, sell, import, and otherwise transfer this
|
|
||||||
software, where such license applies only to those patent
|
|
||||||
claims, already acquired or hereafter acquired, licensable
|
|
||||||
by such copyright holder or contributor that are
|
|
||||||
necessarily infringed by:
|
|
||||||
|
|
||||||
(a) their Contribution(s) (the licensed copyrights of
|
**"Legal Entity"** shall mean the union of the acting entity and all
|
||||||
copyright holders and non-copyrightable additions
|
other entities that control, are controlled by, or are under common
|
||||||
of contributors, in source or binary form) alone;
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
-- or --
|
**"You" (or "Your")** shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
(b) combination of their Contribution(s) with the work
|
**"Source" form** shall mean the preferred form for making modifications,
|
||||||
of authorship to which such Contribution(s) was
|
including but not limited to software source code, documentation source,
|
||||||
added by such copyright holder or contributor, if,
|
and configuration files.
|
||||||
at the time the Contribution is added, such
|
|
||||||
addition causes such combination to be necessarily
|
|
||||||
infringed. The patent license shall not apply to
|
|
||||||
any other combinations which include the
|
|
||||||
Contribution.
|
|
||||||
|
|
||||||
Except as expressly stated above, no rights or licenses
|
**"Object" form** shall mean any form resulting from mechanical
|
||||||
from any copyright holder or contributor is granted under
|
transformation or translation of a Source form, including but not
|
||||||
this license, whether expressly, by implication, estoppel
|
limited to compiled object code, generated documentation, and
|
||||||
or otherwise.
|
conversions to other media types.
|
||||||
|
|
||||||
|
**"Work"** shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
DISCLAIMER
|
**"Derivative Works"** shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
**"Contribution"** shall mean any work of authorship, including the
|
||||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
original version of the Work and any modifications or additions to that
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
Work or Derivative Works thereof, that is intentionally submitted to
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
the Licensor for inclusion in the Work by the copyright owner or by an
|
||||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
individual or Legal Entity authorized to submit on behalf of the
|
||||||
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
copyright owner.
|
||||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED,
|
**"Contributor"** shall mean Licensor and any individual or Legal Entity
|
||||||
LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION,
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY OR
|
subsequently incorporated within the Work.
|
||||||
RELIGIOUS INSTITUTION.
|
|
||||||
|
### 2. Grant of Copyright License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor
|
||||||
|
hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
|
||||||
|
royalty-free, irrevocable copyright license to reproduce, prepare
|
||||||
|
Derivative Works of, publicly display, publicly perform, sublicense,
|
||||||
|
and distribute the Work and such Derivative Works in Source or Object
|
||||||
|
form.
|
||||||
|
|
||||||
|
### 3. Grant of Patent License.
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor
|
||||||
|
hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
|
||||||
|
royalty-free, irrevocable (except as stated in this section) patent
|
||||||
|
license to make, have made, use, offer to sell, sell, import, and
|
||||||
|
otherwise transfer the Work, where such license applies only to those
|
||||||
|
patent claims licensable by such Contributor that are necessarily
|
||||||
|
infringed by their Contribution(s) alone or by combination of their
|
||||||
|
Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
the Work or a Contribution incorporated within the Work constitutes
|
||||||
|
direct or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate as of
|
||||||
|
the date such litigation is filed.
|
||||||
|
|
||||||
|
### 4. Redistribution.
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works
|
||||||
|
thereof in any medium, with or without modifications, and in Source or
|
||||||
|
Object form, provided that You meet the following conditions:
|
||||||
|
|
||||||
|
1. You must give any other recipients of the Work or Derivative Works a
|
||||||
|
copy of this License; and
|
||||||
|
|
||||||
|
2. You must cause any modified files to carry prominent notices stating
|
||||||
|
that You changed the files; and
|
||||||
|
|
||||||
|
3. You must retain, in the Source form of any Derivative Works that You
|
||||||
|
distribute, all copyright, patent, trademark, and attribution notices
|
||||||
|
from the Source form of the Work, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works; and
|
||||||
|
|
||||||
|
4. If the Work includes a "NOTICE" text file as part of its distribution,
|
||||||
|
then any Derivative Works that You distribute must include a readable
|
||||||
|
copy of the attribution notices contained within such NOTICE file,
|
||||||
|
excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works, in at least one of the following places: within a
|
||||||
|
NOTICE text file distributed as part of the Derivative Works; within
|
||||||
|
the Source form or documentation, if provided along with the Derivative
|
||||||
|
Works; or, within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents of the
|
||||||
|
NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative
|
||||||
|
Works that You distribute, alongside or as an addendum to the NOTICE
|
||||||
|
text from the Work, provided that such additional attribution notices
|
||||||
|
cannot be construed as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may
|
||||||
|
provide additional or different license terms and conditions for use,
|
||||||
|
reproduction, or distribution of Your modifications, or for any such
|
||||||
|
Derivative Works as a whole, provided Your use, reproduction, and
|
||||||
|
distribution of the Work otherwise complies with the conditions stated
|
||||||
|
in this License.
|
||||||
|
|
||||||
|
### 5. Submission of Contributions.
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally
|
||||||
|
submitted for inclusion in the Work by You to the Licensor shall be
|
||||||
|
under the terms and conditions of this License, without any additional
|
||||||
|
terms or conditions. Notwithstanding the above, nothing herein shall
|
||||||
|
supersede or modify the terms of any separate license agreement you
|
||||||
|
may have executed with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
### 6. Trademarks.
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names,
|
||||||
|
trademarks, service marks, or product names of the Licensor, except as
|
||||||
|
required for describing the origin of the Work and reproducing the
|
||||||
|
content of the NOTICE file.
|
||||||
|
|
||||||
|
### 7. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor
|
||||||
|
provides the Work (and each Contributor provides its Contributions) on
|
||||||
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||||
|
express or implied, including, without limitation, any warranties or
|
||||||
|
conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
### 8. Limitation of Liability.
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including
|
||||||
|
negligence), contract, or otherwise, unless required by applicable law
|
||||||
|
(such as deliberate and grossly negligent acts) or agreed to in writing,
|
||||||
|
shall any Contributor be liable to You for damages, including any direct,
|
||||||
|
indirect, special, incidental, or consequential damages of any character
|
||||||
|
arising as a result of this License or out of the use or inability to use
|
||||||
|
the Work (including but not limited to damages for loss of goodwill, work
|
||||||
|
stoppage, computer failure or malfunction, or any and all other commercial
|
||||||
|
damages or losses), even if such Contributor has been advised of the
|
||||||
|
possibility of such damages.
|
||||||
|
|
||||||
|
### 9. Accepting Warranty or Additional Liability.
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose
|
||||||
|
to offer, and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this License.
|
||||||
|
However, in accepting such obligations, You may act only on Your own behalf
|
||||||
|
and on Your sole responsibility, not on behalf of any other Contributor,
|
||||||
|
and only if You agree to indemnify, defend, and hold each Contributor
|
||||||
|
harmless for any liability incurred by, or claims asserted against, such
|
||||||
|
Contributor by reason of your accepting any such warranty or additional
|
||||||
|
liability.
|
||||||
|
|||||||
15
README.md
15
README.md
@ -33,3 +33,18 @@ Then add this to your Cargo.toml file.
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rune = { version = "0.3.0", registry = "cybermages" }
|
rune = { version = "0.3.0", registry = "cybermages" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Copyright & License
|
||||||
|
|
||||||
|
Copyright 2025 CyberMages LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this library except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS
|
||||||
|
|||||||
@ -46,7 +46,7 @@ impl std::fmt::Display for MarkdownTokenType
|
|||||||
|
|
||||||
// Define how you want to interpret base tokens
|
// Define how you want to interpret base tokens
|
||||||
pub fn transform(input: &TokenStream<TokenType>)
|
pub fn transform(input: &TokenStream<TokenType>)
|
||||||
-> TokenStream<MarkdownTokenType>
|
-> Result<TokenStream<MarkdownTokenType>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
{
|
{
|
||||||
let mut output = TokenStream::new();
|
let mut output = TokenStream::new();
|
||||||
|
|
||||||
@ -157,11 +157,11 @@ pub fn transform(input: &TokenStream<TokenType>)
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>>
|
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
|
||||||
{
|
{
|
||||||
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
path.push("examples/example.md");
|
path.push("examples/example.md");
|
||||||
|
|||||||
@ -45,7 +45,7 @@ pub struct OptimizedAst<T>
|
|||||||
{
|
{
|
||||||
/// Node data in a linear layout (DFS or BFS order).
|
/// Node data in a linear layout (DFS or BFS order).
|
||||||
pub nodes: Vec<NodeData<T>>,
|
pub nodes: Vec<NodeData<T>>,
|
||||||
/// Each node’s parent, if any.
|
/// Each node's parent, if any.
|
||||||
pub parents: Vec<Option<NodeId>>,
|
pub parents: Vec<Option<NodeId>>,
|
||||||
/// The traversal order the nodes are stored in.
|
/// The traversal order the nodes are stored in.
|
||||||
pub order: TraversalOrder
|
pub order: TraversalOrder
|
||||||
|
|||||||
@ -29,7 +29,7 @@ pub struct LexerError
|
|||||||
pub snippet: Option<String>,
|
pub snippet: Option<String>,
|
||||||
|
|
||||||
/// An optional underlying error that caused this one.
|
/// An optional underlying error that caused this one.
|
||||||
pub source: Option<Box<dyn Error>>
|
pub source: Option<Box<dyn Error + Send + Sync>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LexerError
|
impl LexerError
|
||||||
@ -88,7 +88,7 @@ impl LexerError
|
|||||||
///
|
///
|
||||||
/// This allows you to chain errors for more detailed diagnostics.
|
/// This allows you to chain errors for more detailed diagnostics.
|
||||||
pub fn with_source<E>(mut self, err: E) -> Self
|
pub fn with_source<E>(mut self, err: E) -> Self
|
||||||
where E: Error + 'static
|
where E: Error + Send + Sync + 'static
|
||||||
{
|
{
|
||||||
self.source = Some(Box::new(err));
|
self.source = Some(Box::new(err));
|
||||||
self
|
self
|
||||||
@ -122,6 +122,6 @@ impl Error for LexerError
|
|||||||
/// Returns the underlying cause of this error, if any.
|
/// Returns the underlying cause of this error, if any.
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)>
|
fn source(&self) -> Option<&(dyn Error + 'static)>
|
||||||
{
|
{
|
||||||
self.source.as_ref().map(|e| e.as_ref())
|
self.source.as_deref().map(|e| e as &(dyn Error + 'static))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/lexer.rs
110
src/lexer.rs
@ -12,6 +12,10 @@ use super::token::{TokenStream, TokenType};
|
|||||||
const BUFFER_SIZE: usize = 1024 * 1024;
|
const BUFFER_SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
|
|
||||||
|
/// The Result returned from a lexing function.
|
||||||
|
pub type LexerResult<T> = Result<TokenStream<T>, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// The `Lexer` struct is responsible for performing lexical analysis
|
/// The `Lexer` struct is responsible for performing lexical analysis
|
||||||
/// (tokenization) on text.
|
/// (tokenization) on text.
|
||||||
@ -30,14 +34,14 @@ const BUFFER_SIZE: usize = 1024 * 1024;
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use rune::{Lexer, TokenStream, TokenType};
|
/// use rune::{Lexer, LexerError, LexerResult, TokenStream, TokenType};
|
||||||
///
|
///
|
||||||
/// fn transform(tokens: &TokenStream<TokenType>) -> TokenStream<TokenType>
|
/// fn transform(tokens: &TokenStream<TokenType>) -> LexerResult<TokenType>
|
||||||
/// {
|
/// {
|
||||||
/// tokens.clone()
|
/// Ok(tokens.clone())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let tokens = Lexer::scan_text("Runes += 42", transform).unwrap();
|
/// let tokens = Lexer::scan_text::<_, TokenType, LexerError>("Runes += 42", transform).unwrap();
|
||||||
///
|
///
|
||||||
/// for token in &tokens
|
/// for token in &tokens
|
||||||
/// {
|
/// {
|
||||||
@ -85,19 +89,19 @@ impl Lexer
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns a `LexerError` if the file cannot be opened or read.
|
/// Returns a `LexerError` if the file cannot be opened or read.
|
||||||
pub fn scan_file<P, F, T>(path: P, transform: F)
|
pub fn scan_file<P, F, T>(path: P, transform: F)
|
||||||
-> Result<TokenStream<T>, LexerError>
|
-> LexerResult<T>
|
||||||
where P: AsRef<std::path::Path>,
|
where P: AsRef<std::path::Path>,
|
||||||
F: FnOnce(&TokenStream<TokenType>) -> TokenStream<T>
|
F: FnOnce(&TokenStream<TokenType>) -> LexerResult<T>
|
||||||
{
|
{
|
||||||
let mut cursor = Position::default();
|
let mut cursor = Position::default();
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
let input_file = File::open(&path).map_err(|err| {
|
let input_file = File::open(&path).map_err(|err| {
|
||||||
LexerError::new(
|
Box::new(LexerError::new(
|
||||||
"Unable to open file for Lexical Analysis.",
|
"Unable to open file for Lexical Analysis.",
|
||||||
Span::default(),
|
Span::default(),
|
||||||
Some(path.as_ref().to_path_buf()),
|
Some(path.as_ref().to_path_buf()),
|
||||||
None).with_source(err)
|
None).with_source(err))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let reader = BufReader::with_capacity(BUFFER_SIZE, input_file);
|
let reader = BufReader::with_capacity(BUFFER_SIZE, input_file);
|
||||||
@ -113,13 +117,13 @@ impl Lexer
|
|||||||
}
|
}
|
||||||
Err(_) =>
|
Err(_) =>
|
||||||
{
|
{
|
||||||
return Err(LexerError::new("Unable to read line during \
|
return Err(Box::new(LexerError::new("Unable to read line during \
|
||||||
Lexical Analysis.",
|
Lexical Analysis.",
|
||||||
Span::default(),
|
Span::default(),
|
||||||
Some(path.as_ref()
|
Some(path.as_ref()
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string()),
|
.to_string()),
|
||||||
None));
|
None)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +135,7 @@ impl Lexer
|
|||||||
cursor.column = 0;
|
cursor.column = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(transform(&stream))
|
transform(&stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scans a full in-memory string and produces transformed tokens.
|
/// Scans a full in-memory string and produces transformed tokens.
|
||||||
@ -150,9 +154,9 @@ impl Lexer
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// A `Result<TokenStream<T>, LexerError>` where `T` is the transformed token
|
/// A `Result<TokenStream<T>, LexerError>` where `T` is the transformed token
|
||||||
/// type, or an error.
|
/// type, or an error.
|
||||||
pub fn scan_text<F, T>(text: &str, transform: F)
|
pub fn scan_text<F, T, E>(text: &str, transform: F)
|
||||||
-> Result<TokenStream<T>, LexerError>
|
-> LexerResult<T>
|
||||||
where F: FnOnce(&TokenStream<TokenType>) -> TokenStream<T>
|
where F: FnOnce(&TokenStream<TokenType>) -> LexerResult<T>
|
||||||
{
|
{
|
||||||
let mut cursor = Position::default();
|
let mut cursor = Position::default();
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
@ -177,7 +181,7 @@ impl Lexer
|
|||||||
stream.pop();
|
stream.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(transform(&stream))
|
transform(&stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal method that scans a single line of text into tokens.
|
/// Internal method that scans a single line of text into tokens.
|
||||||
@ -246,3 +250,79 @@ fn get_token_type(curr_char: char) -> TokenType
|
|||||||
_ => TokenType::Unknown
|
_ => TokenType::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::token::{TokenType, TokenStream}; // Adjust import as needed
|
||||||
|
|
||||||
|
// Basic transform function that does nothing — just clones the original stream.
|
||||||
|
fn passthrough_transform(tokens: &TokenStream<TokenType>) -> LexerResult<TokenType> {
|
||||||
|
Ok(tokens.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_text_scan() {
|
||||||
|
let input = "hello world";
|
||||||
|
let result = Lexer::scan_text::<_, TokenType, LexerError>(input, passthrough_transform).unwrap();
|
||||||
|
|
||||||
|
let types: Vec<_> = result.variants.iter().cloned().collect();
|
||||||
|
let expected = vec![
|
||||||
|
TokenType::Text, TokenType::Whitespace, TokenType::Text
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(types, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_numeric_and_symbol_scan() {
|
||||||
|
let input = "123 + 456";
|
||||||
|
let result = Lexer::scan_text::<_, TokenType, LexerError>(input, passthrough_transform).unwrap();
|
||||||
|
|
||||||
|
let types: Vec<_> = result.variants.iter().cloned().collect();
|
||||||
|
let expected = vec![
|
||||||
|
TokenType::Numeric,
|
||||||
|
TokenType::Whitespace,
|
||||||
|
TokenType::Symbol,
|
||||||
|
TokenType::Whitespace,
|
||||||
|
TokenType::Numeric
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(types, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_lines() {
|
||||||
|
let input = "abc\n123";
|
||||||
|
let result = Lexer::scan_text::<_, TokenType, LexerError>(input, passthrough_transform).unwrap();
|
||||||
|
|
||||||
|
let types: Vec<_> = result.variants.iter().cloned().collect();
|
||||||
|
let expected = vec![
|
||||||
|
TokenType::Text,
|
||||||
|
TokenType::Newline,
|
||||||
|
TokenType::Numeric
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(types, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trailing_newline_handling() {
|
||||||
|
let input = "abc";
|
||||||
|
let result = Lexer::scan_text::<_, TokenType, LexerError>(input, passthrough_transform).unwrap();
|
||||||
|
|
||||||
|
// Should NOT end in a Newline since no trailing newline in input
|
||||||
|
assert_ne!(result.lexemes.last().unwrap(), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_input() {
|
||||||
|
let input = "";
|
||||||
|
let result = Lexer::scan_text::<_, TokenType, LexerError>(input, passthrough_transform).unwrap();
|
||||||
|
|
||||||
|
assert!(result.lexemes.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,8 @@ struct TestCase<'a>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn dummy_transform(tokens: &TokenStream<TokenType>) -> TokenStream<TokenType>
|
fn dummy_transform(tokens: &TokenStream<TokenType>)
|
||||||
|
-> Result<TokenStream<TokenType>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
let mut stream: TokenStream<(TokenType, String)> = TokenStream::default();
|
let mut stream: TokenStream<(TokenType, String)> = TokenStream::default();
|
||||||
@ -30,7 +31,7 @@ fn dummy_transform(tokens: &TokenStream<TokenType>) -> TokenStream<TokenType>
|
|||||||
|
|
||||||
stream
|
stream
|
||||||
*/
|
*/
|
||||||
tokens.clone()
|
Ok(tokens.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_temp_file(name: &str, content: &str) -> PathBuf
|
fn write_temp_file(name: &str, content: &str) -> PathBuf
|
||||||
@ -56,8 +57,9 @@ fn cleanup_temp_file(path: &PathBuf)
|
|||||||
fn basic_lexing()
|
fn basic_lexing()
|
||||||
{
|
{
|
||||||
let tokens =
|
let tokens =
|
||||||
Lexer::scan_text("magic runes", dummy_transform).expect("Lexer should \
|
Lexer::scan_text::<_, TokenType, LexerError>("magic runes",
|
||||||
succeed");
|
dummy_transform).expect("Lexer should \
|
||||||
|
succeed");
|
||||||
|
|
||||||
let tokens = tokens.into_iter()
|
let tokens = tokens.into_iter()
|
||||||
.map(|t| (*t.variant, String::from(t.lexeme)))
|
.map(|t| (*t.variant, String::from(t.lexeme)))
|
||||||
@ -75,7 +77,7 @@ fn basic_lexing()
|
|||||||
fn symbols_and_numbers()
|
fn symbols_and_numbers()
|
||||||
{
|
{
|
||||||
let tokens =
|
let tokens =
|
||||||
Lexer::scan_text("13 + 37", dummy_transform).expect("Lexer should \
|
Lexer::scan_text::<_, TokenType, LexerError>("13 + 37", dummy_transform).expect("Lexer should \
|
||||||
succeed");
|
succeed");
|
||||||
|
|
||||||
let tokens = tokens.into_iter()
|
let tokens = tokens.into_iter()
|
||||||
|
|||||||
Reference in New Issue
Block a user