Tholp's bespoke website generator

some simple blocks, bug fixes

Tholp1 39e5639e ff0824cb

-1
src/blocktypes/mod.rs
···
-
//pub mod include;
···
+1 -1
src/macros/clear.rs
···
types::{InputFile, Token},
};
-
pub fn macro_clear(_file: &mut InputFile, _args: &Vec<String>) -> Vec<Token> {
_file.tokens = _file.tokens.split_off(_file.working_index);
_file.working_index = 0;
return Vec::new();
···
types::{InputFile, Token},
};
+
pub fn macro_clear(_file: &mut InputFile, _args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
_file.tokens = _file.tokens.split_off(_file.working_index);
_file.working_index = 0;
return Vec::new();
+1 -1
src/macros/include.rs
···
types::{InputFile, Token},
};
-
pub fn macro_include(_file: &mut InputFile, args: &Vec<String>) -> Vec<Token> {
print!("\nargs: {:?}\n", args);
let mut output = fs::read_to_string(args[0].clone()).expect("File unreadable or missing");
if output.ends_with("\n") {
···
types::{InputFile, Token},
};
+
pub fn macro_include(_file: &mut InputFile, args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
print!("\nargs: {:?}\n", args);
let mut output = fs::read_to_string(args[0].clone()).expect("File unreadable or missing");
if output.ends_with("\n") {
+19 -3
src/macros/mod.rs
···
pub mod clear;
pub mod include;
use super::types::Macro;
use clear::macro_clear;
use include::macro_include;
-
pub static MACRO_LIST: [Macro<'_>; 2] = [
Macro {
-
symbol: "include",
expand: macro_include,
},
Macro {
-
symbol: "clear",
expand: macro_clear,
},
];
···
pub mod clear;
pub mod include;
+
pub mod simple_blocks;
use super::types::Macro;
use clear::macro_clear;
use include::macro_include;
+
use simple_blocks::{macro_comment, macro_repeat};
+
pub static MACRO_LIST: [Macro<'_>; 4] = [
+
// Unscoped
Macro {
+
symbol: "include", // Inserts another file
expand: macro_include,
+
has_scope: false,
},
Macro {
+
symbol: "clear", // Clears text buffer
expand: macro_clear,
+
has_scope: false,
},
+
// Scoped
+
Macro {
+
symbol: "comment", // Nothing
+
expand: macro_comment,
+
has_scope: true,
+
},
+
Macro {
+
symbol: "repeat", // Outputs what its give x number of times
+
expand: macro_repeat,
+
has_scope: true
+
}
];
+31
src/macros/simple_blocks.rs
···
···
+
use crate::types::{InputFile, Token};
+
+
pub fn macro_comment(_file: &mut InputFile, _args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
+
return Vec::new();
+
}
+
+
pub fn macro_null(_file: &mut InputFile, _args: &Vec<String>, scope: &[Token]) -> Vec<Token> {
+
let mut tokens = Vec::new();
+
for tok in scope {
+
tokens.push(tok.clone());
+
}
+
return tokens;
+
}
+
+
pub fn macro_repeat(_file: &mut InputFile, args: &Vec<String>, scope: &[Token]) -> Vec<Token> {
+
let mut count = 0;
+
if args.len() > 0
+
{
+
count = args[0].parse().unwrap_or(0);
+
}
+
+
let mut tokens = Vec::new();
+
for _i in 0..count
+
{
+
for tok in scope
+
{
+
tokens.push(tok.clone());
+
}
+
}
+
return tokens;
+
}
+41 -13
src/main.rs
···
-
mod blocktypes;
mod macros;
mod stringtools;
mod types;
···
io::Write,
process::{exit, Output},
};
-
use stringtools::{collect_arguments, split_keep_delimiters, split_to_tokens, strings_to_tokens};
use types::{InputFile, Macro, Token};
-
static DELIMITERS: [char; 7] = [' ', '\n', '\t', '(', ')', '{', '}'];
fn main() {
let mut files: Vec<types::InputFile> = Vec::new();
···
//file.tokens = strings_to_tokens(split_keep_delimiters(contents), file.filename_input.clone());
file.tokens = split_to_tokens(contents, file.filename_input.clone());
while file.working_index < file.tokens.len() {
//look for macros or blocks
-
//println!(">\"{}\"<", file.tokens[index].contents);
if file.tokens[file.working_index]
.contents
.starts_with(['!', '&'])
{
-
let mut matched: bool = false;
let mut prefix_len = 1;
let mut symbol = file.tokens[file.working_index].contents.clone();
symbol = symbol.trim().to_string();
···
// Check if its a macro
for m in &MACRO_LIST {
if &symbol[prefix_len..] == m.symbol {
-
matched = true;
println!("Found a macro ({})", m.symbol);
-
let (args, tokcount) =
collect_arguments(&file.tokens[file.working_index..]);
let expansion: Vec<Token>;
if ephemeral {
expansion = Vec::new();
} else {
-
expansion = (m.expand)(file, &args);
}
file.tokens.remove(file.working_index);
file.tokens.splice(
-
file.working_index..(file.working_index + tokcount - 1),
-
expansion,
);
}
}
// Check if its a block
// for b in &BLOCK_LIST {}}
}
-
if !matched {
println!(
"Token written as a function but no such function exists \"{}\"",
file.tokens[file.working_index].contents.trim()
);
}
}
-
-
file.working_index += 1;
}
//println!("{:?}", file.tokens);
let mut skid_output: String = "".to_string();
···
mod macros;
mod stringtools;
mod types;
···
io::Write,
process::{exit, Output},
};
+
use stringtools::{
+
collect_arguments, collect_block, split_keep_delimiters, split_to_tokens, strings_to_tokens,
+
trim_whitespace_tokens,
+
};
use types::{InputFile, Macro, Token};
+
static DELIMITERS: [char; 10] = [' ', '\n', '\t', '(', ')', '{', '}', '\\', '\'', '\"'];
fn main() {
let mut files: Vec<types::InputFile> = Vec::new();
···
//file.tokens = strings_to_tokens(split_keep_delimiters(contents), file.filename_input.clone());
file.tokens = split_to_tokens(contents, file.filename_input.clone());
+
//let mut escaped = false;
while file.working_index < file.tokens.len() {
//look for macros or blocks
+
println!(">\"{}\"<", file.tokens[file.working_index].contents);
+
+
if file.tokens[file.working_index].contents == "\\" {
+
file.working_index += 2;
+
continue;
+
}
+
let mut matched_macro: bool = false;
if file.tokens[file.working_index]
.contents
.starts_with(['!', '&'])
{
let mut prefix_len = 1;
let mut symbol = file.tokens[file.working_index].contents.clone();
symbol = symbol.trim().to_string();
···
// Check if its a macro
for m in &MACRO_LIST {
if &symbol[prefix_len..] == m.symbol {
+
matched_macro = true;
println!("Found a macro ({})", m.symbol);
+
let (args, args_tokcount) =
collect_arguments(&file.tokens[file.working_index..]);
let expansion: Vec<Token>;
+
let block_tokcount: usize;
if ephemeral {
expansion = Vec::new();
+
block_tokcount = 0;
} else {
+
if m.has_scope {
+
let block: Vec<Token>;
+
(block, block_tokcount) = collect_block(
+
&file.tokens[(file.working_index + args_tokcount)..],
+
);
+
println!("{}", block_tokcount);
+
expansion = (m.expand)(file, &args, &block[..]);
+
} else {
+
block_tokcount = 0;
+
expansion = (m.expand)(file, &args, &Vec::new()[..]);
+
}
}
+
+
let trimmed = trim_whitespace_tokens(&expansion);
+
file.tokens.remove(file.working_index);
file.tokens.splice(
+
file.working_index
+
..(file.working_index + args_tokcount + block_tokcount - 1),
+
trimmed.iter().cloned(),
);
+
if expansion.len() == 0 && file.working_index > 0 {
+
file.working_index -= 1;
+
}
}
}
// Check if its a block
// for b in &BLOCK_LIST {}}
}
+
if !matched_macro {
println!(
"Token written as a function but no such function exists \"{}\"",
file.tokens[file.working_index].contents.trim()
);
}
}
+
if !matched_macro {
+
file.working_index += 1;
+
}
}
//println!("{:?}", file.tokens);
let mut skid_output: String = "".to_string();
+131 -28
src/stringtools.rs
···
use core::fmt;
-
use std::{fmt::Arguments, ops::Index, process::exit, thread::sleep};
use super::DELIMITERS;
use crate::types::Token;
pub fn collect_arguments(tokens: &[Token]) -> (Vec<String>, usize) {
//let mut output = Vec::new();
-
let mut split_tokens = Vec::new();
-
for tok in tokens {
-
for s in split_keep_delimiters(tok.contents.clone()) {
-
split_tokens.push(s);
-
}
-
}
let mut quoted: bool = false;
let mut entered: bool = false;
···
let mut in_token_count = 0;
-
for tok in split_tokens {
-
in_token_count += 1;
-
if tok.starts_with([' ', '\t']) && !quoted {
continue;
}
-
if !entered && tok.starts_with('(') {
entered = true;
continue;
}
···
continue;
}
-
if !quoted && tok.starts_with(')') {
break;
}
let mut i = 0;
-
while i < tok.len() {
-
let c = tok.chars().nth(i).unwrap();
i += 1;
if c == '\"' {
···
return (args, in_token_count);
}
// Theres no std function to have the delimiters be their own element in the out vector
// clean it up a bit here
pub fn split_keep_delimiters(instr: String) -> Vec<String> {
···
}
// Need to do some special case stuff so you can macros without spaces between
pub fn split_to_tokens(instr: String, origin_file: String) -> Vec<Token> {
let split = split_keep_delimiters(instr);
let mut new_split: Vec<String> = Vec::new();
···
return strings_to_tokens(new_split, origin_file);
}
-
pub fn next_nonwhitespace_token(tokens: &Vec<Token>, index: usize) -> (bool, usize) {
-
while index < tokens.len() {
-
if tokens[index].contents.starts_with([' ', '\t', '\n']) {
-
continue;
}
-
return (true, index);
}
-
return (false, 0);
}
-
pub trait IsDelimiter {
-
fn is_delimiter(&self) -> bool;
}
-
impl IsDelimiter for char {
-
fn is_delimiter(&self) -> bool {
-
for d in DELIMITERS {
-
if *self == d {
-
return true;
}
}
-
return false;
}
}
···
use core::fmt;
+
use std::{
+
ascii::escape_default, f32::consts::E, fmt::Arguments, ops::Index, process::exit, thread::sleep,
+
};
use super::DELIMITERS;
use crate::types::Token;
pub fn collect_arguments(tokens: &[Token]) -> (Vec<String>, usize) {
+
// Arguments vec and number of tokens consumed
//let mut output = Vec::new();
+
// let mut split_tokens = Vec::new();
+
// for tok in tokens {
+
// for s in split_keep_delimiters(tok.contents.clone()) {
+
// split_tokens.push(s);
+
// }
+
//}
let mut quoted: bool = false;
let mut entered: bool = false;
···
let mut in_token_count = 0;
+
for tok in tokens {
+
in_token_count += 1; // This could be a problem if it something got split above..
+
let contents = &tok.contents;
+
if contents.starts_with([' ', '\t']) && !quoted {
continue;
}
+
if !entered && contents.starts_with('(') {
entered = true;
continue;
}
···
continue;
}
+
if !quoted && contents.starts_with(')') {
break;
}
let mut i = 0;
+
while i < contents.len() {
+
let c = contents.chars().nth(i).unwrap();
i += 1;
if c == '\"' {
···
return (args, in_token_count);
}
+
pub fn collect_block(tokens: &[Token]) -> (Vec<Token>, usize) {
+
let mut entered = false;
+
let mut tokens_consumed: usize = 0;
+
let mut entering_bracket_count = 0;
+
let mut exiting_bracket_count = 0;
+
let mut scope_count = 0; //incremented by '{{{', decremented by '}}}'
+
let mut escaped = false;
+
+
let mut block: Vec<Token> = Vec::new();
+
+
// We dont really care about doing anything that in the block right now
+
// maybe have the Token struct contain scope level later?
+
for tok in tokens {
+
tokens_consumed += 1;
+
+
println!(
+
"Collecting: \"{}\" is brak: {}",
+
tok.contents,
+
tok.contents == "{"
+
);
+
// println!(
+
// "ebrack: {}, scope: {}, entered: {}, escaped: {}",
+
// entering_bracket_count, scope_count, entered, escaped
+
//);
+
if !entered {
+
if tok.contents.is_only_whitespace() {
+
continue;
+
}
+
if tok.contents != "{"
+
// Expected block start, got garbage
+
{
+
println!("Expected \'{{{{{{\' got \'{}\'", tok.contents);
+
return (Vec::new(), 0);
+
}
+
}
+
+
if escaped {
+
escaped = false;
+
entering_bracket_count = 0;
+
exiting_bracket_count = 0;
+
block.push(tok.clone());
+
continue;
+
}
+
+
// Scope Start
+
+
if tok.contents == "{" {
+
entering_bracket_count += 1;
+
} else {
+
entering_bracket_count = 0;
+
}
+
// Scope End
+
if tok.contents == "}" {
+
exiting_bracket_count += 1;
+
if exiting_bracket_count == 3 {
+
scope_count -= 1;
+
exiting_bracket_count = 0;
+
if scope_count == 0 {
+
//pop the last 2 brackets off and return
+
block.pop();
+
block.pop();
+
break;
+
}
+
}
+
} else {
+
exiting_bracket_count = 0;
+
}
+
if tok.contents == "\\" {
+
escaped = true;
+
} else {
+
if entered {
+
block.push(tok.clone());
+
}
+
}
+
if entering_bracket_count == 3 {
+
scope_count += 1;
+
entering_bracket_count = 0;
+
+
entered = true;
+
}
+
}
+
return (block, tokens_consumed);
+
}
+
// Theres no std function to have the delimiters be their own element in the out vector
// clean it up a bit here
pub fn split_keep_delimiters(instr: String) -> Vec<String> {
···
}
// Need to do some special case stuff so you can macros without spaces between
+
// (something like "stuff!insert(..)" is split to ["stuff","!insert(..)"] so it can be acted on later)
pub fn split_to_tokens(instr: String, origin_file: String) -> Vec<Token> {
let split = split_keep_delimiters(instr);
let mut new_split: Vec<String> = Vec::new();
···
return strings_to_tokens(new_split, origin_file);
}
+
pub fn trim_whitespace_tokens(tokens: &[Token]) -> &[Token] {
+
let mut start: usize = 0;
+
let mut end: usize = tokens.len();
+
for tok in tokens {
+
if !tok.contents.is_only_whitespace() {
+
break;
}
+
start += 1;
+
}
+
+
for tok in tokens.iter().rev() {
+
if !tok.contents.is_only_whitespace() {
+
break;
+
}
+
end -= 1;
+
}
+
+
if start < end {
+
return &tokens[start..end];
+
} else {
+
return &[];
}
}
+
pub trait OnlyWhitespace {
+
fn is_only_whitespace(&self) -> bool;
}
+
impl OnlyWhitespace for String {
+
fn is_only_whitespace(&self) -> bool {
+
for c in self.chars() {
+
if !c.is_whitespace() {
+
return false;
}
}
+
return true;
}
}
+13 -3
src/types.rs
···
pub struct Token {
pub contents: String,
-
pub origin_file: String,
pub line_number: u32,
}
···
pub block_edges: Vec<BlockEdge>,
}
-
type MacroExpansion = fn(&mut InputFile, &Vec<String>) -> Vec<Token>;
pub struct Macro<'a> {
pub symbol: &'a str,
pub expand: MacroExpansion,
-
//pub always_ephemeral: bool, // This wont be included from other files
}
impl InputFile {
···
return self.contents.clone();
}
}
···
pub struct Token {
pub contents: String,
+
pub origin_file: String, //TODO: make this an index so we dont have the same string in memory for every single word
pub line_number: u32,
}
···
pub block_edges: Vec<BlockEdge>,
}
+
type MacroExpansion = fn(&mut InputFile, &Vec<String>, &[Token]) -> Vec<Token>;
pub struct Macro<'a> {
pub symbol: &'a str,
pub expand: MacroExpansion,
+
pub has_scope: bool, //takes blocks of text input as well as parameters using {{...}}
}
impl InputFile {
···
return self.contents.clone();
}
}
+
+
impl Clone for Token {
+
fn clone(&self) -> Self {
+
return Token::new(
+
self.contents.clone(),
+
self.origin_file.clone(),
+
self.line_number,
+
);
+
}
+
}