mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
224 lines
6.1 KiB
Rust
224 lines
6.1 KiB
Rust
use automata::{
|
|
automata::npda::{self, NPDA},
|
|
loader::{self, Span, Spanned, lexer::Lexer},
|
|
};
|
|
|
|
use wasm_bindgen::prelude::wasm_bindgen;
|
|
|
|
#[wasm_bindgen]
|
|
pub fn test() {
|
|
panic!()
|
|
}
|
|
|
|
#[wasm_bindgen(start)]
|
|
pub fn main() {}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn init() {
|
|
console_error_panic_hook::set_once();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn silly(machine: &str, input: &str) {
|
|
let table = match npda::TransitionTable::load_table(machine) {
|
|
Ok((ok, logs)) => {
|
|
for log in logs.displayable() {
|
|
println!("{log}")
|
|
}
|
|
ok
|
|
}
|
|
Err(logs) => {
|
|
for log in logs.displayable() {
|
|
println!("{log}")
|
|
}
|
|
return;
|
|
}
|
|
};
|
|
|
|
println!("running on: '{input}'");
|
|
let mut simulator = npda::Simulator::begin(input, table);
|
|
loop {
|
|
match simulator.step() {
|
|
npda::SimulatorResult::Pending => {}
|
|
npda::SimulatorResult::Reject => {
|
|
println!("REJECTED");
|
|
break;
|
|
}
|
|
npda::SimulatorResult::Accept(npda) => {
|
|
println!("ACCEPT: {npda:?}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
#[derive(Clone, Copy)]
|
|
pub enum Kind {
|
|
Ident = "ident",
|
|
Keyword = "keyword",
|
|
Error = "error",
|
|
Comment = "comment",
|
|
Punc = "punc",
|
|
|
|
LPar = "lpar",
|
|
LBrace = "lbrace",
|
|
LBracket = "lbracket",
|
|
|
|
RPar = "rpar",
|
|
RBrace = "rbrace",
|
|
RBracket = "rbracket",
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
#[derive(Clone, Copy)]
|
|
pub struct Tok {
|
|
pub start: usize,
|
|
pub end: usize,
|
|
pub scope_level: usize,
|
|
pub kind: Kind,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn lex(input: &str) -> Vec<Tok> {
|
|
let mut scope_level = 0;
|
|
let mut index_utf16 = 0;
|
|
let mut index_utf8 = 0;
|
|
Lexer::new(input)
|
|
.map(|Spanned(tok, Span(start_utf8, end_utf8))| {
|
|
let since_last = &input[index_utf8..start_utf8];
|
|
let since_start = &input[start_utf8..end_utf8];
|
|
|
|
index_utf8 = end_utf8;
|
|
let start = index_utf16 + since_last.chars().map(char::len_utf16).sum::<usize>();
|
|
let end = start + since_start.chars().map(char::len_utf16).sum::<usize>();
|
|
index_utf16 = end;
|
|
|
|
let Ok(tok) = tok else {
|
|
return Tok {
|
|
start,
|
|
end,
|
|
kind: Kind::Error,
|
|
scope_level,
|
|
};
|
|
};
|
|
use automata::loader::lexer::Token;
|
|
let kind = match tok {
|
|
Token::LPar => Kind::LPar,
|
|
Token::RPar => Kind::RPar,
|
|
Token::LBrace => Kind::LBrace,
|
|
Token::RBrace => Kind::RBrace,
|
|
Token::LBracket => Kind::LBracket,
|
|
Token::RBracket => Kind::RBracket,
|
|
Token::Tilde => Kind::Keyword,
|
|
Token::Eq => Kind::Punc,
|
|
Token::Comma => Kind::Punc,
|
|
Token::Or => Kind::Punc,
|
|
Token::Plus => Kind::Punc,
|
|
Token::Star => Kind::Punc,
|
|
Token::And => Kind::Punc,
|
|
Token::LSmallArrow => Kind::Punc,
|
|
Token::LBigArrow => Kind::Punc,
|
|
Token::Comment(_) => Kind::Comment,
|
|
Token::Ident(_)
|
|
if input[..start_utf8]
|
|
.split("\n")
|
|
.last()
|
|
.unwrap_or_default()
|
|
.trim()
|
|
.is_empty() =>
|
|
{
|
|
Kind::Keyword
|
|
}
|
|
Token::Ident(
|
|
loader::EPSILON_LOWER
|
|
| "epsilon"
|
|
| loader::DELTA_LOWER
|
|
| "delta"
|
|
| loader::GAMMA_UPPER
|
|
| "gamma"
|
|
| loader::GAMMA_LOWER
|
|
| loader::SIGMA_UPPER
|
|
| "sigma",
|
|
) => Kind::Keyword,
|
|
Token::Ident(_) => Kind::Ident,
|
|
Token::LineEnd => Kind::Punc,
|
|
};
|
|
|
|
let scope_level = match kind {
|
|
Kind::LPar | Kind::LBrace | Kind::LBracket => {
|
|
scope_level = scope_level.saturating_add(1);
|
|
scope_level.saturating_sub(1)
|
|
}
|
|
Kind::RPar | Kind::RBrace | Kind::RBracket => {
|
|
scope_level = scope_level.saturating_sub(1);
|
|
scope_level
|
|
}
|
|
_ => scope_level,
|
|
};
|
|
Tok {
|
|
start,
|
|
end,
|
|
kind,
|
|
scope_level,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
#[derive(Clone, Copy)]
|
|
pub enum LogLevel {
|
|
Info = "info",
|
|
Warning = "warning",
|
|
Error = "error",
|
|
}
|
|
|
|
#[wasm_bindgen(getter_with_clone)]
|
|
#[derive(Clone)]
|
|
pub struct CompileLog {
|
|
pub level: LogLevel,
|
|
pub message: String,
|
|
pub start: Option<usize>,
|
|
pub end: Option<usize>,
|
|
}
|
|
|
|
#[wasm_bindgen(getter_with_clone)]
|
|
pub struct CompileResult {
|
|
pub log: Vec<CompileLog>,
|
|
pub log_formatted: String,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn compile(input: &str) -> CompileResult {
|
|
let log = match npda::TransitionTable::load_table(input) {
|
|
Ok((_, logs)) => logs,
|
|
Err(logs) => logs,
|
|
};
|
|
|
|
use std::fmt::Write;
|
|
let log_formatted = log.displayable().fold(String::new(), |mut s, e| {
|
|
write!(&mut s, "{e}").unwrap();
|
|
s
|
|
});
|
|
|
|
let log = log
|
|
.into_entries()
|
|
.map(|e| CompileLog {
|
|
level: match e.level {
|
|
loader::log::LogLevel::Info => LogLevel::Info,
|
|
loader::log::LogLevel::Warning => LogLevel::Warning,
|
|
loader::log::LogLevel::Error => LogLevel::Error,
|
|
},
|
|
message: e.message,
|
|
start: e
|
|
.span
|
|
.map(|span| input[..span.0].chars().map(char::len_utf16).count()),
|
|
end: e
|
|
.span
|
|
.map(|span| input[..span.1].chars().map(char::len_utf16).count()),
|
|
})
|
|
.collect();
|
|
|
|
CompileResult { log, log_formatted }
|
|
}
|