Tholp's bespoke website generator
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Initial Commit, !include and basic tooling

Tholp1 fdd1aaef

+337
+4
.gitignore
··· 1 + /target 2 + *.out 3 + .vscode 4 + Cargo.lock
+7
Cargo.toml
··· 1 + [package] 2 + name = "skidmark" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + markdown = "0.3.0"
+3
a.sk
··· 1 + Skidmark progress: functions are prefixed with '!' 2 + theres currently only one so far and its !include("b.sk") 3 +
+4
b.sk
··· 1 + used to include files inside other files, isnt that cool? 2 + 3 + Functions prefixed with '&' are "Ephemeral" and will only be expanded if it comes from within the same file thats being proccessed 4 + &include("c.sk")
+1
c.sk
··· 1 + See?
+1
src/blocktypes/mod.rs
··· 1 + //pub mod include;
+10
src/error.rs
··· 1 + pub fn exit_error(msg : String) 2 + { 3 + println!("[Error]" + msg); 4 + exit(1); 5 + } 6 + 7 + pub fn warn(msg: String) 8 + { 9 + 10 + }
+17
src/macros/include.rs
··· 1 + use std::{env::Args, fs}; 2 + 3 + use crate::{ 4 + stringtools::{split_keep_delimiters, strings_to_tokens}, 5 + types::{InputFile, Token}, 6 + }; 7 + 8 + pub fn macro_include(_file: &InputFile, args: &Vec<String>) -> Vec<Token> { 9 + print!("\nargs: {:?}\n", args); 10 + let mut output = fs::read_to_string(args[0].clone()).expect("File unreadable or missing"); 11 + if output.ends_with("\n") { 12 + output.pop(); 13 + } //remove trailing newline 14 + 15 + let split_output = split_keep_delimiters(output); 16 + return strings_to_tokens(split_output, args[0].clone()); 17 + }
+10
src/macros/mod.rs
··· 1 + pub mod include; 2 + use super::types::Macro; 3 + 4 + use include::macro_include; 5 + 6 + pub static MACRO_LIST: [Macro<'_>; 1] = [Macro { 7 + symbol: "include", 8 + expand: macro_include, 9 + //always_ephemeral: false, 10 + }];
+95
src/main.rs
··· 1 + mod blocktypes; 2 + mod macros; 3 + mod stringtools; 4 + mod types; 5 + 6 + use macros::{ 7 + include::{self, macro_include}, 8 + MACRO_LIST, 9 + }; 10 + use std::{ 11 + env, 12 + fs::{self, File}, 13 + io::Write, 14 + process::{exit, Output}, 15 + }; 16 + use stringtools::{collect_arguments, split_keep_delimiters, strings_to_tokens}; 17 + use types::{InputFile, Macro, Token}; 18 + 19 + static DELIMITERS: [char; 7] = [' ', '\n', '\t', '(', ')', '{', '}']; 20 + 21 + fn main() { 22 + let mut files: Vec<types::InputFile> = Vec::new(); 23 + let mut args: Vec<String> = env::args().collect(); 24 + args.remove(0); 25 + 26 + for file in args.iter() { 27 + let mut new_file = types::InputFile::new(); 28 + new_file.filename_in = file.to_string(); 29 + new_file.filename_out = file.to_string() + ".out"; 30 + files.push(new_file); 31 + } 32 + println!("{:?}", args); 33 + for f in &mut files { 34 + process_file(f); 35 + } 36 + } 37 + 38 + fn process_file(file: &mut InputFile) { 39 + let contents = fs::read_to_string(&file.filename_in).expect("File unreadable or missing"); 40 + //println!("{}\n {}", f.filename_out, contents); 41 + 42 + file.tokens = strings_to_tokens(split_keep_delimiters(contents), file.filename_in.clone()); 43 + 44 + let mut index = 0; 45 + 46 + while index < file.tokens.len() { 47 + //look for macros or blocks 48 + //println!(">\"{}\"<", file.tokens[index].contents); 49 + 50 + if file.tokens[index].contents.starts_with(['!', '&']) { 51 + let mut matched = false; 52 + 53 + for m in &MACRO_LIST { 54 + if &file.tokens[index].contents.trim()[1..] == m.symbol { 55 + matched = true; 56 + println!("Found a macro ({})", m.symbol); 57 + let mut ephemeral = false; 58 + if file.tokens[index].contents.starts_with('&') 59 + && file.tokens[index].origin_file != file.filename_in 60 + { 61 + println!("Skipping Ephermal macro from included file."); 62 + ephemeral = true; 63 + } 64 + 65 + let (args, tokcount) = collect_arguments(&file.tokens[index..]); 66 + let expansion: Vec<Token>; 67 + if ephemeral { 68 + expansion = Vec::new(); 69 + } else { 70 + expansion = (m.expand)(&file, &args); 71 + } 72 + file.tokens.remove(index); 73 + file.tokens.splice(index..(index + tokcount - 1), expansion); 74 + } 75 + } 76 + 77 + // for b in &BLOCK_LIST {} 78 + 79 + if !matched { 80 + println!( 81 + "Token written as a function but no such function exists \"{}\"", 82 + file.tokens[index].contents.trim() 83 + ); 84 + } 85 + } 86 + 87 + index += 1; 88 + } 89 + //println!("{:?}", file.tokens); 90 + let mut full_output: String = "".to_string(); 91 + for t in &file.tokens { 92 + full_output += &t.contents; 93 + } 94 + fs::write(&file.filename_out, full_output).expect("Couldn't write to file"); 95 + }
+124
src/stringtools.rs
··· 1 + use core::fmt; 2 + use std::{fmt::Arguments, ops::Index, process::exit}; 3 + 4 + use super::DELIMITERS; 5 + use crate::types::Token; 6 + 7 + pub fn collect_arguments(tokens: &[Token]) -> (Vec<String>, usize) { 8 + //let mut output = Vec::new(); 9 + let mut split_tokens = Vec::new(); 10 + for tok in tokens { 11 + for s in split_keep_delimiters(tok.contents.clone()) { 12 + split_tokens.push(s); 13 + } 14 + } 15 + 16 + let mut quoted: bool = false; 17 + let mut entered: bool = false; 18 + let mut arg = "".to_string(); 19 + let mut args: Vec<String> = Vec::new(); 20 + 21 + let mut in_token_count = 0; 22 + 23 + for tok in split_tokens { 24 + in_token_count += 1; 25 + if tok.starts_with([' ', '\t']) && !quoted { 26 + continue; 27 + } 28 + 29 + if !entered && tok.starts_with('(') { 30 + entered = true; 31 + continue; 32 + } 33 + 34 + if !entered { 35 + continue; 36 + } 37 + 38 + if !quoted && tok.starts_with(')') { 39 + break; 40 + } 41 + 42 + let mut i = 0; 43 + while i < tok.len() { 44 + let c = tok.chars().nth(i).unwrap(); 45 + i += 1; 46 + 47 + if c == '\"' { 48 + quoted = !quoted; 49 + continue; 50 + } 51 + 52 + arg.push(c); 53 + } 54 + 55 + if !quoted { 56 + args.push(arg.clone()); 57 + arg.clear(); 58 + } 59 + } 60 + 61 + return (args, in_token_count); 62 + } 63 + 64 + // Theres no std function to have the delimiters be their own element in the out vector 65 + // clean it up a bit here 66 + pub fn split_keep_delimiters(instr: String) -> Vec<String> { 67 + let split: Vec<&str> = instr.split_inclusive(DELIMITERS).collect(); 68 + let mut output = Vec::new(); 69 + 70 + for s in split { 71 + if s.ends_with(DELIMITERS) { 72 + let (token, ending) = s.split_at(s.len() - 1); 73 + if token.len() > 0 { 74 + output.push(token.to_string()); 75 + } 76 + output.push(ending.to_string()); 77 + } else { 78 + output.push(s.to_string()); 79 + } 80 + } 81 + return output; 82 + } 83 + 84 + pub fn strings_to_tokens(instrings: Vec<String>, origin_file: String) -> Vec<Token> { 85 + let mut tokens = Vec::new(); 86 + let mut linecount: u32 = 1; 87 + 88 + for str in instrings { 89 + let currentline = linecount; 90 + for char in str.chars() { 91 + if char == '\n' { 92 + linecount += 1; 93 + } 94 + } 95 + let token: Token = Token::new(str, origin_file.clone(), currentline); 96 + tokens.push(token); 97 + } 98 + 99 + return tokens; 100 + } 101 + pub fn next_nonwhitespace_token(tokens: &Vec<Token>, index: usize) -> (bool, usize) { 102 + while index < tokens.len() { 103 + if tokens[index].contents.starts_with([' ', '\t', '\n']) { 104 + continue; 105 + } 106 + return (true, index); 107 + } 108 + return (false, 0); 109 + } 110 + 111 + pub trait IsDelimiter { 112 + fn is_delimiter(&self) -> bool; 113 + } 114 + 115 + impl IsDelimiter for char { 116 + fn is_delimiter(&self) -> bool { 117 + for d in DELIMITERS { 118 + if *self == d { 119 + return true; 120 + } 121 + } 122 + return false; 123 + } 124 + }
+61
src/types.rs
··· 1 + use std::sync::Mutex; 2 + 3 + pub struct Token { 4 + pub contents: String, 5 + pub origin_file: String, 6 + pub line_number: u32, 7 + } 8 + 9 + pub enum BlockEdgeType { 10 + FileStart, 11 + FileEnd, 12 + Start, 13 + End, 14 + } 15 + 16 + // A 'Block' is what im calling the enclosed scope of a macro 17 + pub struct BlockEdge { 18 + pub edge_type: BlockEdgeType, 19 + pub tokens_to_next_edge: u64, 20 + } 21 + 22 + pub struct InputFile { 23 + pub filename_in: String, 24 + pub filename_out: String, 25 + pub tokens: Vec<Token>, 26 + pub block_edges: Vec<BlockEdge>, 27 + } 28 + 29 + type MacroExpansion = fn(&InputFile, &Vec<String>) -> Vec<Token>; 30 + pub struct Macro<'a> { 31 + pub symbol: &'a str, 32 + pub expand: MacroExpansion, 33 + //pub always_ephemeral: bool, // This wont be included from other files 34 + } 35 + 36 + impl InputFile { 37 + pub fn new() -> InputFile { 38 + InputFile { 39 + filename_in: "".to_string(), 40 + filename_out: "".to_string(), 41 + tokens: Vec::new(), 42 + block_edges: Vec::new(), 43 + } 44 + } 45 + } 46 + 47 + impl Token { 48 + pub fn new(contents: String, origin_file: String, line_number: u32) -> Token { 49 + Token { 50 + contents: contents, 51 + origin_file: origin_file, 52 + line_number: line_number, 53 + } 54 + } 55 + } 56 + 57 + impl ToString for Token { 58 + fn to_string(&self) -> String { 59 + return self.contents.clone(); 60 + } 61 + }