modularize
This commit is contained in:
parent
038f6fe03d
commit
40fdac182f
4 changed files with 173 additions and 182 deletions
38
src/inst.rs
Normal file
38
src/inst.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use core::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Inst<'a> {
|
||||
Set(&'a str),
|
||||
Var(&'a str),
|
||||
Num(f64),
|
||||
Call,
|
||||
Block(Vec<Inst<'a>>),
|
||||
}
|
||||
|
||||
impl fmt::Display for Inst<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Inst::Set(v) => write!(f, "→{v}"),
|
||||
Inst::Var(v) => write!(f, "{v}"),
|
||||
Inst::Num(n) => write!(f, "{n}"),
|
||||
Inst::Call => write!(f, "!"),
|
||||
Inst::Block(insts) => {
|
||||
write!(f, "(")?;
|
||||
if insts.len() == 1 {
|
||||
write!(f, "{}", insts[0])?;
|
||||
} else if !insts.is_empty() {
|
||||
for window in insts.windows(2) {
|
||||
match window {
|
||||
[a, Inst::Call] => write!(f, "{a}")?,
|
||||
[a, _] => write!(f, "{a} ")?,
|
||||
[last] => write!(f, "{last}")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
write!(f, "{}", insts.last().unwrap())?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
src/main.rs
164
src/main.rs
|
|
@ -1,167 +1,13 @@
|
|||
use core::fmt;
|
||||
use crate::{inst::Inst, parse::parse};
|
||||
use std::io::{self, Read};
|
||||
|
||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
use chumsky::{
|
||||
input::{Stream, ValueInput},
|
||||
prelude::*,
|
||||
};
|
||||
use logos::Logos;
|
||||
|
||||
#[derive(Logos, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[logos(skip r"[ \t\n\r\f]+")]
|
||||
enum Token<'a> {
|
||||
#[token("->")]
|
||||
#[token("→")]
|
||||
Set,
|
||||
|
||||
#[token("(")]
|
||||
OpenParen,
|
||||
|
||||
#[token(")")]
|
||||
CloseParen,
|
||||
|
||||
#[regex(r"[a-zA-Z0-9+*/%.:=-]+")]
|
||||
VarOrNum(&'a str),
|
||||
|
||||
#[token("!")]
|
||||
Call,
|
||||
|
||||
Error,
|
||||
}
|
||||
|
||||
impl fmt::Display for Token<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Set => write!(f, "→"),
|
||||
Token::OpenParen => write!(f, "("),
|
||||
Token::CloseParen => write!(f, ")"),
|
||||
Token::VarOrNum(s) => write!(f, "{s}"),
|
||||
Token::Call => write!(f, "!"),
|
||||
Token::Error => write!(f, "ERROR"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Inst<'a> {
|
||||
Set(&'a str),
|
||||
Var(&'a str),
|
||||
Num(f64),
|
||||
Call,
|
||||
Block(Vec<Inst<'a>>),
|
||||
}
|
||||
|
||||
impl fmt::Display for Inst<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Inst::Set(v) => write!(f, "→{v}"),
|
||||
Inst::Var(v) => write!(f, "{v}"),
|
||||
Inst::Num(n) => write!(f, "{n}"),
|
||||
Inst::Call => write!(f, "!"),
|
||||
Inst::Block(insts) => {
|
||||
write!(f, "(")?;
|
||||
if insts.len() == 1 {
|
||||
write!(f, "{}", insts[0])?;
|
||||
} else if !insts.is_empty() {
|
||||
for window in insts.windows(2) {
|
||||
match window {
|
||||
[a, Inst::Call] => write!(f, "{a}")?,
|
||||
[a, _] => write!(f, "{a} ")?,
|
||||
[last] => write!(f, "{last}")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
write!(f, "{}", insts.last().unwrap())?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parser<'tokens, 'src: 'tokens, I>()
|
||||
-> impl Parser<'tokens, I, Vec<Inst<'src>>, extra::Err<Rich<'tokens, Token<'src>>>>
|
||||
where
|
||||
I: ValueInput<'tokens, Token = Token<'src>, Span = SimpleSpan>,
|
||||
{
|
||||
let inst = recursive(|inst| {
|
||||
let block = inst
|
||||
.clone()
|
||||
.repeated()
|
||||
.collect()
|
||||
.map(Inst::Block)
|
||||
.delimited_by(
|
||||
just(Token::OpenParen),
|
||||
just(Token::CloseParen).or(end().validate(|(), e, emitter| {
|
||||
emitter.emit(Rich::custom(e.span(), "Expected close paren"));
|
||||
Token::Error
|
||||
})),
|
||||
);
|
||||
|
||||
choice((
|
||||
just(Token::Set).ignore_then(inst.validate(|v: Inst<'_>, e, emitter| {
|
||||
if let Inst::Var(v) = v {
|
||||
Inst::Set(v)
|
||||
} else {
|
||||
emitter.emit(Rich::custom(
|
||||
e.span(),
|
||||
"Set target must be a variable".to_string(),
|
||||
));
|
||||
Inst::Set("invalid")
|
||||
}
|
||||
})),
|
||||
just(Token::Call).to(Inst::Call),
|
||||
select! {
|
||||
Token::VarOrNum(s) => s.parse::<f64>().map_or_else(|_| Inst::Var(s), Inst::Num),
|
||||
},
|
||||
block,
|
||||
just(Token::Error).validate(|_, e, emitter| {
|
||||
emitter.emit(Rich::custom(e.span(), "Invalid token".to_string()));
|
||||
Inst::Call
|
||||
}),
|
||||
))
|
||||
});
|
||||
|
||||
inst.or(
|
||||
just(Token::CloseParen).try_map(|_, span| Err(Rich::custom(span, "Unmatched close paren")))
|
||||
)
|
||||
.repeated()
|
||||
.collect()
|
||||
}
|
||||
mod inst;
|
||||
mod parse;
|
||||
|
||||
fn main() {
|
||||
let mut src = String::new();
|
||||
let _ = io::stdin().read_to_string(&mut src).unwrap();
|
||||
|
||||
let token_iter = Token::lexer(&src).spanned().map(|(tok, span)| match tok {
|
||||
Ok(tok) => (tok, span.into()),
|
||||
Err(()) => (Token::Error, span.into()),
|
||||
});
|
||||
|
||||
let token_stream =
|
||||
Stream::from_iter(token_iter).map((0..src.len()).into(), |(t, s): (_, _)| (t, s));
|
||||
|
||||
let source_filename = "input";
|
||||
|
||||
match parser().parse(token_stream).into_result() {
|
||||
Ok(insts) => println!("{}!\n{insts:#?}", Inst::Block(insts.clone())),
|
||||
Err(errs) => {
|
||||
for err in errs {
|
||||
Report::build(
|
||||
ReportKind::Error,
|
||||
(source_filename, err.span().into_range()),
|
||||
)
|
||||
.with_config(ariadne::Config::new().with_index_type(ariadne::IndexType::Byte))
|
||||
.with_label(
|
||||
Label::new((source_filename, err.span().into_range()))
|
||||
.with_message(err.reason())
|
||||
.with_color(Color::Red),
|
||||
)
|
||||
.finish()
|
||||
.eprint((source_filename, Source::from(&src)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
let insts = parse(&src);
|
||||
println!("{}!\n{insts:#?}", Inst::Block(insts.clone()));
|
||||
}
|
||||
|
|
|
|||
130
src/parse.rs
Normal file
130
src/parse.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use core::fmt;
|
||||
use std::process;
|
||||
|
||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
use chumsky::{
|
||||
input::{Stream, ValueInput},
|
||||
prelude::*,
|
||||
};
|
||||
use logos::Logos;
|
||||
|
||||
use crate::inst::Inst;
|
||||
|
||||
#[derive(Logos, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[logos(skip r"[ \t\n\r\f]+")]
|
||||
enum Token<'a> {
|
||||
#[token("->")]
|
||||
#[token("→")]
|
||||
Set,
|
||||
|
||||
#[token("(")]
|
||||
OpenParen,
|
||||
|
||||
#[token(")")]
|
||||
CloseParen,
|
||||
|
||||
#[regex(r"[a-zA-Z0-9+*/%.:=-]+")]
|
||||
VarOrNum(&'a str),
|
||||
|
||||
#[token("!")]
|
||||
Call,
|
||||
|
||||
Error,
|
||||
}
|
||||
|
||||
impl fmt::Display for Token<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Set => write!(f, "→"),
|
||||
Token::OpenParen => write!(f, "("),
|
||||
Token::CloseParen => write!(f, ")"),
|
||||
Token::VarOrNum(s) => write!(f, "{s}"),
|
||||
Token::Call => write!(f, "!"),
|
||||
Token::Error => write!(f, "ERROR"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parser<'tokens, 'src: 'tokens, I>()
|
||||
-> impl Parser<'tokens, I, Vec<Inst<'src>>, extra::Err<Rich<'tokens, Token<'src>>>>
|
||||
where
|
||||
I: ValueInput<'tokens, Token = Token<'src>, Span = SimpleSpan>,
|
||||
{
|
||||
let inst = recursive(|inst| {
|
||||
let block = inst
|
||||
.clone()
|
||||
.repeated()
|
||||
.collect()
|
||||
.map(Inst::Block)
|
||||
.delimited_by(
|
||||
just(Token::OpenParen),
|
||||
just(Token::CloseParen).or(end().validate(|(), e, emitter| {
|
||||
emitter.emit(Rich::custom(e.span(), "Expected close paren"));
|
||||
Token::Error
|
||||
})),
|
||||
);
|
||||
|
||||
choice((
|
||||
just(Token::Set).ignore_then(inst.validate(|v: Inst<'_>, e, emitter| {
|
||||
if let Inst::Var(v) = v {
|
||||
Inst::Set(v)
|
||||
} else {
|
||||
emitter.emit(Rich::custom(
|
||||
e.span(),
|
||||
"Set target must be a variable".to_string(),
|
||||
));
|
||||
Inst::Set("invalid")
|
||||
}
|
||||
})),
|
||||
just(Token::Call).to(Inst::Call),
|
||||
select! {
|
||||
Token::VarOrNum(s) => s.parse::<f64>().map_or_else(|_| Inst::Var(s), Inst::Num),
|
||||
},
|
||||
block,
|
||||
just(Token::Error).validate(|_, e, emitter| {
|
||||
emitter.emit(Rich::custom(e.span(), "Invalid token".to_string()));
|
||||
Inst::Call
|
||||
}),
|
||||
))
|
||||
});
|
||||
|
||||
inst.or(
|
||||
just(Token::CloseParen).try_map(|_, span| Err(Rich::custom(span, "Unmatched close paren")))
|
||||
)
|
||||
.repeated()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse(src: &str) -> Vec<Inst<'_>> {
|
||||
let token_iter = Token::lexer(src).spanned().map(|(tok, span)| match tok {
|
||||
Ok(tok) => (tok, span.into()),
|
||||
Err(()) => (Token::Error, span.into()),
|
||||
});
|
||||
|
||||
let token_stream =
|
||||
Stream::from_iter(token_iter).map((0..src.len()).into(), |(t, s): (_, _)| (t, s));
|
||||
|
||||
let source_filename = "input";
|
||||
|
||||
match parser().parse(token_stream).into_result() {
|
||||
Ok(insts) => insts,
|
||||
Err(errs) => {
|
||||
for err in errs {
|
||||
Report::build(
|
||||
ReportKind::Error,
|
||||
(source_filename, err.span().into_range()),
|
||||
)
|
||||
.with_config(ariadne::Config::new().with_index_type(ariadne::IndexType::Byte))
|
||||
.with_label(
|
||||
Label::new((source_filename, err.span().into_range()))
|
||||
.with_message(err.reason())
|
||||
.with_color(Color::Red),
|
||||
)
|
||||
.finish()
|
||||
.eprint((source_filename, Source::from(&src)))
|
||||
.unwrap();
|
||||
}
|
||||
process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
xclip
23
xclip
|
|
@ -1,23 +0,0 @@
|
|||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
|
||||
Running `target/debug/puyo-lang`
|
||||
Error:
|
||||
╭─[ input:1:7 ]
|
||||
│
|
||||
1 │ a b ->1 c ->2 ->a ->(1 2 3)
|
||||
│ ┬
|
||||
│ ╰── Set target must be a variable
|
||||
───╯
|
||||
Error:
|
||||
╭─[ input:1:13 ]
|
||||
│
|
||||
1 │ a b ->1 c ->2 ->a ->(1 2 3)
|
||||
│ ┬
|
||||
│ ╰── Set target must be a variable
|
||||
───╯
|
||||
Error:
|
||||
╭─[ input:1:21 ]
|
||||
│
|
||||
1 │ a b ->1 c ->2 ->a ->(1 2 3)
|
||||
│ ───┬───
|
||||
│ ╰───── Set target must be a variable
|
||||
───╯
|
||||
Loading…
Reference in a new issue