cl 0.0.2: MAJOR ERGONOMIC BOOST
Broke frontend into its own library, "cl-frontend" - Frontend is pretty :D - Included sample fibonacci implementation Deprecated conlang::ast::Visitor in favor of bespoke traits - Rust traits are super cool. - The Interpreter is currently undergoing a major rewrite Added preliminary type-path support to the parser - Currently incomplete: type paths must end in Never..? Pretty printer is now even prettier - conlang::ast now exports all relevant AST nodes, since there are no namespace collisions any more
This commit is contained in:
parent
9b7cf9c017
commit
79fda16788
@ -1,10 +1,10 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["libconlang"]
|
members = ["libconlang", "cl-frontend"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
repository = "https://git.soft.fish/j/Conlang"
|
repository = "https://git.soft.fish/j/Conlang"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
authors = ["John Breaux <j@soft.fish>"]
|
authors = ["John Breaux <j@soft.fish>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
13
cl-frontend/Cargo.toml
Normal file
13
cl-frontend/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "cl-frontend"
|
||||||
|
repository.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
conlang = { path = "../libconlang" }
|
483
cl-frontend/src/lib.rs
Normal file
483
cl-frontend/src/lib.rs
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
//! Utilities for cl-frontend
|
||||||
|
|
||||||
|
pub mod args {
|
||||||
|
use crate::cli::Mode;
|
||||||
|
use std::{
|
||||||
|
io::{stdin, IsTerminal},
|
||||||
|
ops::Deref,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Args {
|
||||||
|
pub path: Option<PathBuf>, // defaults None
|
||||||
|
pub repl: bool, // defaults true if stdin is terminal
|
||||||
|
pub mode: Mode, // defaults Interpret
|
||||||
|
}
|
||||||
|
const HELP: &str = "[( --repl | --no-repl )] [( -f | --file ) <filename>]";
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Args { path: None, repl: stdin().is_terminal(), mode: Mode::Interpret }
|
||||||
|
}
|
||||||
|
pub fn parse(mut self) -> Option<Self> {
|
||||||
|
let mut args = std::env::args();
|
||||||
|
let name = args.next().unwrap_or_default();
|
||||||
|
let mut unknown = false;
|
||||||
|
while let Some(arg) = args.next() {
|
||||||
|
match arg.deref() {
|
||||||
|
"--repl" => self.repl = true,
|
||||||
|
"--no-repl" => self.repl = false,
|
||||||
|
"-f" | "--file" => self.path = args.next().map(PathBuf::from),
|
||||||
|
"-m" | "--mode" => {
|
||||||
|
self.mode = args.next().unwrap_or_default().parse().unwrap_or_default()
|
||||||
|
}
|
||||||
|
arg => {
|
||||||
|
eprintln!("Unknown argument: {arg}");
|
||||||
|
unknown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if unknown {
|
||||||
|
println!("Usage: {name} {HELP}");
|
||||||
|
None?
|
||||||
|
}
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
/// Returns the path to a file, if one was specified
|
||||||
|
pub fn path(&self) -> Option<&Path> {
|
||||||
|
self.path.as_deref()
|
||||||
|
}
|
||||||
|
/// Returns whether to start a REPL session or not
|
||||||
|
pub fn repl(&self) -> bool {
|
||||||
|
self.repl
|
||||||
|
}
|
||||||
|
/// Returns the repl Mode
|
||||||
|
pub fn mode(&self) -> Mode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for Args {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod program {
|
||||||
|
use std::io::{Result as IOResult, Write};
|
||||||
|
|
||||||
|
use conlang::{
|
||||||
|
ast::preamble::{expression::Expr, *},
|
||||||
|
interpreter::{error::IResult, Interpreter},
|
||||||
|
lexer::Lexer,
|
||||||
|
parser::{error::PResult, Parser},
|
||||||
|
pretty_printer::{PrettyPrintable, Printer},
|
||||||
|
resolver::{error::TyResult, Resolve, Resolver},
|
||||||
|
token::Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Tokenized {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Parsed {
|
||||||
|
Program(Start),
|
||||||
|
Expr(Expr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Tokenized> for Parsed {
|
||||||
|
type Error = conlang::parser::error::Error;
|
||||||
|
fn try_from(value: Tokenized) -> Result<Self, Self::Error> {
|
||||||
|
let mut parser = Parser::new(value.tokens);
|
||||||
|
let ast = parser.parse()?;
|
||||||
|
//if let Ok(ast) = ast {
|
||||||
|
//return
|
||||||
|
Ok(Self::Program(ast))
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Ok(Self::Expr(parser.reset().parse_expr()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Program<Variant> {
|
||||||
|
data: Variant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program<Tokenized> {
|
||||||
|
pub fn new(input: &str) -> Result<Self, Vec<conlang::lexer::error::Error>> {
|
||||||
|
let mut tokens = vec![];
|
||||||
|
let mut errors = vec![];
|
||||||
|
for token in Lexer::new(input) {
|
||||||
|
match token {
|
||||||
|
Ok(token) => tokens.push(token),
|
||||||
|
Err(error) => errors.push(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(Self { data: Tokenized { tokens } })
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn tokens(&self) -> &[Token] {
|
||||||
|
&self.data.tokens
|
||||||
|
}
|
||||||
|
pub fn parse(self) -> PResult<Program<Parsed>> {
|
||||||
|
Ok(Program { data: Parsed::try_from(self.data)? })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program<Parsed> {
|
||||||
|
pub fn resolve(&mut self, resolver: &mut Resolver) -> TyResult<()> {
|
||||||
|
match &mut self.data {
|
||||||
|
Parsed::Program(start) => start.resolve(resolver),
|
||||||
|
Parsed::Expr(expr) => expr.resolve(resolver),
|
||||||
|
}
|
||||||
|
.map(|ty| println!("{ty}"))
|
||||||
|
}
|
||||||
|
/// Runs the [Program] in the specified [Interpreter]
|
||||||
|
pub fn run(&self, interpreter: &mut Interpreter) -> IResult<()> {
|
||||||
|
match &self.data {
|
||||||
|
Parsed::Program(start) => interpreter.interpret(start),
|
||||||
|
Parsed::Expr(expr) => {
|
||||||
|
for value in interpreter.eval(expr)? {
|
||||||
|
println!("{value}")
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyPrintable for Program<Parsed> {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match &self.data {
|
||||||
|
Parsed::Program(value) => value.visit(p),
|
||||||
|
Parsed::Expr(value) => value.visit(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod cli {
|
||||||
|
use conlang::{
|
||||||
|
interpreter::Interpreter, pretty_printer::PrettyPrintable, resolver::Resolver, token::Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
args::Args,
|
||||||
|
program::{Parsed, Program, Tokenized},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
error::Error,
|
||||||
|
io::{stdin, stdout, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ANSI color escape sequences
|
||||||
|
const ANSI_RED: &str = "\x1b[31m";
|
||||||
|
const ANSI_GREEN: &str = "\x1b[32m";
|
||||||
|
const ANSI_CYAN: &str = "\x1b[36m";
|
||||||
|
const ANSI_BRIGHT_BLUE: &str = "\x1b[94m";
|
||||||
|
const ANSI_BRIGHT_MAGENTA: &str = "\x1b[95m";
|
||||||
|
// const ANSI_BRIGHT_CYAN: &str = "\x1b[96m";
|
||||||
|
const ANSI_RESET: &str = "\x1b[0m";
|
||||||
|
const ANSI_OUTPUT: &str = "\x1b[38;5;117m";
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum CLI {
|
||||||
|
Repl(Repl),
|
||||||
|
File { mode: Mode, path: PathBuf },
|
||||||
|
Stdin { mode: Mode },
|
||||||
|
}
|
||||||
|
impl From<Args> for CLI {
|
||||||
|
fn from(value: Args) -> Self {
|
||||||
|
let Args { path, repl, mode } = value;
|
||||||
|
match (repl, path) {
|
||||||
|
(_, Some(path)) => Self::File { mode, path },
|
||||||
|
(true, None) => Self::Repl(Repl { mode, ..Default::default() }),
|
||||||
|
(false, None) => Self::Stdin { mode },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLI {
|
||||||
|
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
use std::{fs, io};
|
||||||
|
match self {
|
||||||
|
CLI::Repl(repl) => repl.repl(),
|
||||||
|
CLI::File { mode, ref path } => {
|
||||||
|
// read file
|
||||||
|
Self::no_repl(*mode, Some(path), &fs::read_to_string(path)?)
|
||||||
|
}
|
||||||
|
CLI::Stdin { mode } => {
|
||||||
|
Self::no_repl(*mode, None, &io::read_to_string(io::stdin())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn no_repl(mode: Mode, path: Option<&Path>, code: &str) {
|
||||||
|
let program = Program::new(code);
|
||||||
|
match (mode, program) {
|
||||||
|
(Mode::Tokenize, Ok(program)) => {
|
||||||
|
for token in program.tokens() {
|
||||||
|
if let Some(path) = path {
|
||||||
|
print!("{}:", path.display());
|
||||||
|
}
|
||||||
|
print_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Mode::Beautify, Ok(program)) => Self::beautify(program),
|
||||||
|
(Mode::Resolve, Ok(program)) => Self::resolve(program, Default::default()),
|
||||||
|
(Mode::Interpret, Ok(program)) => Self::interpret(program, Default::default()),
|
||||||
|
(_, Err(errors)) => {
|
||||||
|
for error in errors {
|
||||||
|
if let Some(path) = path {
|
||||||
|
eprint!("{}:", path.display());
|
||||||
|
}
|
||||||
|
eprintln!("{error}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn beautify(program: Program<Tokenized>) {
|
||||||
|
match program.parse() {
|
||||||
|
Ok(program) => program.print(),
|
||||||
|
Err(e) => eprintln!("{e}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn resolve(program: Program<Tokenized>, mut resolver: Resolver) {
|
||||||
|
let mut program = match program.parse() {
|
||||||
|
Ok(program) => program,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(e) = program.resolve(&mut resolver) {
|
||||||
|
eprintln!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn interpret(program: Program<Tokenized>, mut interpreter: Interpreter) {
|
||||||
|
let program = match program.parse() {
|
||||||
|
Ok(program) => program,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(e) = program.run(&mut interpreter) {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(e) = interpreter.call("main", &[]) {
|
||||||
|
eprintln!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The CLI's operating mode
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Mode {
|
||||||
|
Tokenize,
|
||||||
|
Beautify,
|
||||||
|
Resolve,
|
||||||
|
#[default]
|
||||||
|
Interpret,
|
||||||
|
}
|
||||||
|
impl Mode {
|
||||||
|
pub fn ansi_color(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Mode::Tokenize => ANSI_BRIGHT_BLUE,
|
||||||
|
Mode::Beautify => ANSI_BRIGHT_MAGENTA,
|
||||||
|
Mode::Resolve => ANSI_GREEN,
|
||||||
|
Mode::Interpret => ANSI_CYAN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromStr for Mode {
|
||||||
|
type Err = Infallible;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Infallible> {
|
||||||
|
Ok(match s {
|
||||||
|
"i" | "interpret" => Mode::Interpret,
|
||||||
|
"b" | "beautify" | "p" | "pretty" => Mode::Beautify,
|
||||||
|
"r" | "resolve" | "typecheck" => Mode::Resolve,
|
||||||
|
"t" | "tokenize" => Mode::Tokenize,
|
||||||
|
_ => Mode::Interpret,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the interactive interpreter
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Repl {
|
||||||
|
prompt_again: &'static str, // " ?>"
|
||||||
|
prompt_begin: &'static str, // "cl>"
|
||||||
|
prompt_error: &'static str, // "! >"
|
||||||
|
interpreter: Interpreter,
|
||||||
|
resolver: Resolver,
|
||||||
|
mode: Mode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Repl {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
prompt_begin: "cl>",
|
||||||
|
prompt_again: " ?>",
|
||||||
|
prompt_error: "! >",
|
||||||
|
interpreter: Default::default(),
|
||||||
|
resolver: Default::default(),
|
||||||
|
mode: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prompt functions
|
||||||
|
impl Repl {
|
||||||
|
pub fn prompt_begin(&self) {
|
||||||
|
print!(
|
||||||
|
"{}{} {ANSI_RESET}",
|
||||||
|
self.mode.ansi_color(),
|
||||||
|
self.prompt_begin
|
||||||
|
);
|
||||||
|
let _ = stdout().flush();
|
||||||
|
}
|
||||||
|
pub fn prompt_again(&self) {
|
||||||
|
print!(
|
||||||
|
"{}{} {ANSI_RESET}",
|
||||||
|
self.mode.ansi_color(),
|
||||||
|
self.prompt_again
|
||||||
|
);
|
||||||
|
let _ = stdout().flush();
|
||||||
|
}
|
||||||
|
pub fn prompt_error(&self, err: &impl Error) {
|
||||||
|
println!("{ANSI_RED}{} {err}{ANSI_RESET}", self.prompt_error)
|
||||||
|
}
|
||||||
|
pub fn begin_output(&self) {
|
||||||
|
print!("{ANSI_OUTPUT}")
|
||||||
|
}
|
||||||
|
fn reprompt(&self, buf: &mut String) {
|
||||||
|
self.prompt_begin();
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// The actual REPL
|
||||||
|
impl Repl {
|
||||||
|
/// Constructs a new [Repl] with the provided [Mode]
|
||||||
|
pub fn new(mode: Mode) -> Self {
|
||||||
|
Self { mode, ..Default::default() }
|
||||||
|
}
|
||||||
|
/// Runs the main REPL loop
|
||||||
|
pub fn repl(&mut self) {
|
||||||
|
let mut buf = String::new();
|
||||||
|
self.prompt_begin();
|
||||||
|
while let Ok(len) = stdin().read_line(&mut buf) {
|
||||||
|
// Exit the loop
|
||||||
|
if len == 0 {
|
||||||
|
println!();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.begin_output();
|
||||||
|
// Process mode-change commands
|
||||||
|
if self.command(&buf) {
|
||||||
|
self.reprompt(&mut buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Lex the buffer, or reset and output the error
|
||||||
|
let code = match Program::new(&buf) {
|
||||||
|
Ok(code) => code,
|
||||||
|
Err(e) => {
|
||||||
|
for error in e {
|
||||||
|
eprintln!("{error}");
|
||||||
|
}
|
||||||
|
self.reprompt(&mut buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Tokenize mode doesn't require valid parse, so it gets processed first
|
||||||
|
if self.mode == Mode::Tokenize {
|
||||||
|
self.tokenize(&code);
|
||||||
|
self.reprompt(&mut buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Parse code and dispatch to the proper function
|
||||||
|
match (len, code.parse()) {
|
||||||
|
// If the code is OK, run it and print any errors
|
||||||
|
(_, Ok(mut code)) => {
|
||||||
|
self.dispatch(&mut code);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
// If the user types two newlines, print syntax errors
|
||||||
|
(1, Err(e)) => {
|
||||||
|
self.prompt_error(&e);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
// Otherwise, ask for more input
|
||||||
|
_ => {
|
||||||
|
self.prompt_again();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.prompt_begin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help(&self) {
|
||||||
|
println!(
|
||||||
|
"Commands:\n- $tokens\n Tokenize Mode:\n Outputs information derived by the Lexer\n- $pretty\n Beautify Mode:\n Pretty-prints the input\n- $type\n Resolve Mode:\n Attempts variable resolution and type-checking on the input\n- $run\n Interpret Mode:\n Interprets the input using Conlang\'s work-in-progress interpreter\n- $mode\n Prints the current mode\n- $help\n Prints this help message"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn command(&mut self, line: &str) -> bool {
|
||||||
|
match line.trim() {
|
||||||
|
"$pretty" => self.mode = Mode::Beautify,
|
||||||
|
"$tokens" => self.mode = Mode::Tokenize,
|
||||||
|
"$type" => self.mode = Mode::Resolve,
|
||||||
|
"$run" => self.mode = Mode::Interpret,
|
||||||
|
"$mode" => print!("{:?} Mode", self.mode),
|
||||||
|
"$help" => self.help(),
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
/// Dispatches calls to repl functions based on the program
|
||||||
|
fn dispatch(&mut self, code: &mut Program<Parsed>) {
|
||||||
|
match self.mode {
|
||||||
|
Mode::Tokenize => (),
|
||||||
|
Mode::Beautify => self.beautify(code),
|
||||||
|
Mode::Resolve => self.typecheck(code),
|
||||||
|
Mode::Interpret => self.interpret(code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn tokenize(&mut self, code: &Program<Tokenized>) {
|
||||||
|
for token in code.tokens() {
|
||||||
|
print_token(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn interpret(&mut self, code: &Program<Parsed>) {
|
||||||
|
if let Err(e) = code.run(&mut self.interpreter) {
|
||||||
|
self.prompt_error(&e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn typecheck(&mut self, code: &mut Program<Parsed>) {
|
||||||
|
if let Err(e) = code.resolve(&mut self.resolver) {
|
||||||
|
self.prompt_error(&e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn beautify(&mut self, code: &Program<Parsed>) {
|
||||||
|
code.print()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_token(t: &Token) {
|
||||||
|
println!(
|
||||||
|
"{:02}:{:02}: {:#19} │{}│",
|
||||||
|
t.line(),
|
||||||
|
t.col(),
|
||||||
|
t.ty(),
|
||||||
|
t.data(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
9
cl-frontend/src/main.rs
Normal file
9
cl-frontend/src/main.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use cl_frontend::{args::Args, cli::CLI};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
// parse args
|
||||||
|
let args = Args::new().parse().unwrap_or_default();
|
||||||
|
let mut cli = CLI::from(args);
|
||||||
|
cli.run()
|
||||||
|
}
|
26
grammar.ebnf
26
grammar.ebnf
@ -1,7 +1,14 @@
|
|||||||
(* Conlang Expression Grammar *)
|
(* Conlang Expression Grammar *)
|
||||||
Start = Program ;
|
Start = Program ;
|
||||||
Program = Stmt* EOI ;
|
Program = Stmt* EOI ;
|
||||||
|
(* TODO:
|
||||||
|
- Replace Program with Module
|
||||||
|
Module = Decl* EOI ;
|
||||||
|
- Move Fn and Let into "Decl":
|
||||||
|
Decl = Fn | Let ;
|
||||||
|
- allow Decl | ExprStmt in Stmt:
|
||||||
|
Stmt = Decl | Expr ';' ;
|
||||||
|
*)
|
||||||
(* literal *)
|
(* literal *)
|
||||||
Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
|
Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
|
||||||
Bool = "true" | "false" ;
|
Bool = "true" | "false" ;
|
||||||
@ -10,11 +17,22 @@ Identifier = IDENTIFIER ;
|
|||||||
(* # Statements *)
|
(* # Statements *)
|
||||||
(* statement *)
|
(* statement *)
|
||||||
Stmt = Fn | Let | Expr ';' ;
|
Stmt = Fn | Let | Expr ';' ;
|
||||||
Let = "let" "mut"? Identifier (':' Type)? ('=' Expr)? ';' ;
|
Let = "let" Name ('=' Expr)? ';' ;
|
||||||
Fn = "fn" Identifier '(' Params? ')' Block ;
|
Fn = "fn" Identifier '(' Params? ')' Block ;
|
||||||
(* TODO: Type system *)
|
(* TODO: Type system *)
|
||||||
Params = (Param ',')* Param? ;
|
Params = (Name ',')* Name? ;
|
||||||
Param = Identifier (*':' Type *) ;
|
Name = "mut"? Identifier (':' Type )? ;
|
||||||
|
|
||||||
|
(* # Type Expressions *)
|
||||||
|
(* types *)
|
||||||
|
TypeExpr = Never | Empty | TypeTuple | PathExpr ;
|
||||||
|
Never = '!' ;
|
||||||
|
Empty = '(' ')' ;
|
||||||
|
TypeTuple = '(' (TypeExpr ',')* TypeExpr? ')' ;
|
||||||
|
|
||||||
|
PathExpr = '::'? PathPart ;
|
||||||
|
PathPart = "super" | Identifier ;
|
||||||
|
|
||||||
|
|
||||||
(* # Expressions *)
|
(* # Expressions *)
|
||||||
(* expression *)
|
(* expression *)
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
//! This example grabs input from stdin, lexes it, parses it, and prints the AST
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
use conlang::{lexer::Lexer, parser::Parser, pretty_printer::PrettyPrintable, token::Token};
|
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
io::{stdin, stdout, IsTerminal, Read, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let conf = Config::new();
|
|
||||||
if conf.paths.is_empty() {
|
|
||||||
take_stdin()?;
|
|
||||||
} else {
|
|
||||||
for path in conf.paths.iter().map(PathBuf::as_path) {
|
|
||||||
parse(&std::fs::read_to_string(path)?, Some(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
paths: Vec<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
fn new() -> Self {
|
|
||||||
Config { paths: std::env::args().skip(1).map(PathBuf::from).collect() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_stdin() -> Result<(), Box<dyn Error>> {
|
|
||||||
const PROMPT: &str = "> ";
|
|
||||||
if stdin().is_terminal() {
|
|
||||||
print!("{PROMPT}");
|
|
||||||
stdout().flush()?;
|
|
||||||
for line in stdin().lines() {
|
|
||||||
let line = line?;
|
|
||||||
if !line.is_empty() {
|
|
||||||
parse(&line, None);
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
print!("{PROMPT}");
|
|
||||||
stdout().flush()?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parse(&std::io::read_to_string(stdin())?, None)
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(file: &str, path: Option<&Path>) {
|
|
||||||
use conlang::parser::error::Error;
|
|
||||||
match Parser::from(Lexer::new(file)).parse() {
|
|
||||||
Ok(ast) => ast.print(),
|
|
||||||
Err(e) if e.start().is_some() => print!("{:?}:{}", path.unwrap_or(Path::new("-")), e),
|
|
||||||
Err(e) => print!("{e}"),
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
//! This example grabs input from stdin or a file, lexes it, parses it, and interprets it
|
|
||||||
use conlang::{
|
|
||||||
ast::{expression::Expr, Start},
|
|
||||||
interpreter::Interpreter,
|
|
||||||
lexer::Lexer,
|
|
||||||
parser::Parser,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
io::{stdin, stdout, IsTerminal, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let conf = Config::new();
|
|
||||||
if conf.paths.is_empty() {
|
|
||||||
take_stdin()?;
|
|
||||||
} else {
|
|
||||||
for path in conf.paths.iter().map(PathBuf::as_path) {
|
|
||||||
run_file(&std::fs::read_to_string(path)?, Some(path))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
paths: Vec<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
fn new() -> Self {
|
|
||||||
Config { paths: std::env::args().skip(1).map(PathBuf::from).collect() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! prompt {
|
|
||||||
($($t:tt)*) => {{
|
|
||||||
let mut out = stdout().lock();
|
|
||||||
out.write_all(b"\x1b[36m")?;
|
|
||||||
write!(out, $($t)*)?;
|
|
||||||
out.write_all(b"\x1b[0m")?;
|
|
||||||
out.flush()
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_stdin() -> Result<(), Box<dyn Error>> {
|
|
||||||
const CONLANG: &str = "cl>";
|
|
||||||
const MOREPLS: &str = " ?>";
|
|
||||||
if stdin().is_terminal() {
|
|
||||||
let mut interpreter = Interpreter::new();
|
|
||||||
let mut buf = String::new();
|
|
||||||
prompt!("{CONLANG} ")?;
|
|
||||||
while let Ok(len) = stdin().read_line(&mut buf) {
|
|
||||||
let code = Program::parse(&buf);
|
|
||||||
match (len, code) {
|
|
||||||
// Exit the loop
|
|
||||||
(0, _) => {
|
|
||||||
println!();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// If the code is OK, run it and print any errors
|
|
||||||
(_, Ok(code)) => {
|
|
||||||
let _ = code.run(&mut interpreter).map_err(|e| eprintln!("{e}"));
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
// If the user types two newlines, print syntax errors
|
|
||||||
(1, Err(e)) => {
|
|
||||||
eprintln!("{e}");
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
// Otherwise, ask for more input
|
|
||||||
_ => {
|
|
||||||
prompt!("{MOREPLS} ")?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prompt!("{CONLANG} ")?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
run_file(&std::io::read_to_string(stdin())?, None)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_file(file: &str, path: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut interpreter = Interpreter::new();
|
|
||||||
match Parser::from(Lexer::new(file)).parse() {
|
|
||||||
Ok(ast) => {
|
|
||||||
interpreter.interpret(&ast)?;
|
|
||||||
println!("{}", interpreter.call("main", &[])?)
|
|
||||||
},
|
|
||||||
Err(e) if e.start().is_some() => print!("{:?}:{}", path.unwrap_or(Path::new("-")), e),
|
|
||||||
Err(e) => print!("{e}"),
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Program {
|
|
||||||
Program(Start),
|
|
||||||
Expr(Expr),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Program {
|
|
||||||
fn parse(input: &str) -> conlang::parser::error::PResult<Self> {
|
|
||||||
let ast = Parser::from(Lexer::new(input)).parse();
|
|
||||||
if let Ok(ast) = ast {
|
|
||||||
return Ok(Self::Program(ast));
|
|
||||||
}
|
|
||||||
Ok(Self::Expr(Parser::from(Lexer::new(input)).parse_expr()?))
|
|
||||||
}
|
|
||||||
fn run(&self, interpreter: &mut Interpreter) -> conlang::interpreter::error::IResult<()> {
|
|
||||||
match self {
|
|
||||||
Program::Program(start) => interpreter.interpret(start),
|
|
||||||
Program::Expr(expr) => {
|
|
||||||
for value in interpreter.eval(expr)? {
|
|
||||||
println!("{value}")
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,19 +11,16 @@
|
|||||||
//! See [statement], [literal], and [expression] for more information.
|
//! See [statement], [literal], and [expression] for more information.
|
||||||
|
|
||||||
pub mod preamble {
|
pub mod preamble {
|
||||||
|
#![allow(deprecated)]
|
||||||
//! Common imports for working with the [ast](super)
|
//! Common imports for working with the [ast](super)
|
||||||
pub use super::{
|
pub use super::{
|
||||||
expression::{
|
expression::{call::*, control::*, math::*, tuple::*, *},
|
||||||
self,
|
literal::*,
|
||||||
call::*,
|
path::*,
|
||||||
control,
|
|
||||||
math::{self, operator},
|
|
||||||
tuple::*,
|
|
||||||
},
|
|
||||||
literal,
|
|
||||||
statement::*,
|
statement::*,
|
||||||
|
types::*,
|
||||||
visitor::Visitor,
|
visitor::Visitor,
|
||||||
Identifier, Program, Start,
|
*,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,47 +40,9 @@ pub struct Program(pub Vec<statement::Stmt>);
|
|||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier)
|
/// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier)
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Identifier(pub String);
|
pub struct Identifier {
|
||||||
|
pub name: String,
|
||||||
pub mod todo {
|
pub index: Option<usize>,
|
||||||
//! temporary storage for pending expression work. \
|
|
||||||
//! when an item is in progress, remove it from todo.
|
|
||||||
//!
|
|
||||||
//! # General TODOs:
|
|
||||||
//! - [x] Implement support for storing items in the AST
|
|
||||||
//! - [ ] Keep track of the source location of each node
|
|
||||||
//! - [ ] Implement paths
|
|
||||||
//! - [x] Implement functions
|
|
||||||
//! - [ ] Implement structs
|
|
||||||
//! - [ ] Implement enums
|
|
||||||
//! - [ ] Implement implementation
|
|
||||||
//! - [ ] Store token spans in AST
|
|
||||||
pub mod path {
|
|
||||||
//! Path support
|
|
||||||
//! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | `foo::bar::*`)
|
|
||||||
//!
|
|
||||||
//! Path resolution will be vital to the implementation of structs, enums, impl blocks,
|
|
||||||
//! traits, modules, etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod structure {
|
|
||||||
//! Struct support
|
|
||||||
//! - [ ] Add struct declaration expression (returns a struct declaration)
|
|
||||||
//! - [ ] Add struct value expression (returns a struct value)
|
|
||||||
//! - [ ] Add struct update syntax (yippee!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod enumeration {
|
|
||||||
//! Enum support
|
|
||||||
//! - [ ] Add enum declaration expression (returns an enum declaration)
|
|
||||||
//! - [ ] Add enum value expression (returns an enum value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod implementation {
|
|
||||||
//! Impl block support
|
|
||||||
//! - [ ] Add impl block expression? Statement?
|
|
||||||
//! - [ ] Add member function call expression
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod literal {
|
pub mod literal {
|
||||||
@ -150,6 +109,7 @@ pub mod statement {
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
expression::{Block, Expr},
|
expression::{Block, Expr},
|
||||||
|
types::TypeExpr,
|
||||||
Identifier,
|
Identifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -164,7 +124,7 @@ pub mod statement {
|
|||||||
Let(Let),
|
Let(Let),
|
||||||
/// Contains a function declaration
|
/// Contains a function declaration
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` [`Block`]
|
/// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` `Args...` `')'` [`Block`]
|
||||||
Fn(FnDecl),
|
Fn(FnDecl),
|
||||||
/// Contains an expression statement
|
/// Contains an expression statement
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
@ -177,26 +137,104 @@ pub mod statement {
|
|||||||
/// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;`
|
/// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Let {
|
pub struct Let {
|
||||||
pub name: Identifier,
|
pub name: Name,
|
||||||
pub mutable: bool,
|
|
||||||
pub ty: Option<Identifier>,
|
|
||||||
pub init: Option<Expr>,
|
pub init: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains a function declaration
|
/// Contains a function declaration
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`FnDecl`] := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'`
|
/// [`FnDecl`] := `"fn"` [`Identifier`] `'('` `Args...` `')'`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FnDecl {
|
pub struct FnDecl {
|
||||||
pub name: Identifier,
|
pub name: Name,
|
||||||
pub args: Vec<Identifier>,
|
pub args: Vec<Name>,
|
||||||
pub body: Block,
|
pub body: Block,
|
||||||
// TODO: Store type information
|
// TODO: Store type information
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains the name, mutability, and type information for a [Let] or [FnDecl]
|
||||||
|
/// # Syntax
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Name {
|
||||||
|
pub name: Identifier,
|
||||||
|
/// The mutability of the [Name]. Functions are never mutable.
|
||||||
|
pub mutable: bool,
|
||||||
|
/// The [type](TypeExpr)
|
||||||
|
pub ty: Option<TypeExpr>,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Create closure, transmute fndecl into a name and closure
|
// TODO: Create closure, transmute fndecl into a name and closure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod path {
|
||||||
|
//! Paths
|
||||||
|
//!
|
||||||
|
//! A Path Expression refers to an item, either local or module-scoped.
|
||||||
|
|
||||||
|
use super::Identifier;
|
||||||
|
|
||||||
|
/// A path to an item in a module
|
||||||
|
/// # Syntax
|
||||||
|
/// [`Path`]` := "::"? `[`PathPart`]` ("::" `[`PathPart`]`)*`
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Path {
|
||||||
|
pub absolute: bool,
|
||||||
|
pub parts: Vec<PathPart>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component of a [`TypePath`]
|
||||||
|
/// # Syntax
|
||||||
|
/// [`PathPart`]` := "super" | `[`Identifier`]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PathPart {
|
||||||
|
PathSuper,
|
||||||
|
PathSelf,
|
||||||
|
PathIdent(Identifier),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod types {
|
||||||
|
//! # Types
|
||||||
|
//!
|
||||||
|
//! The [Type Expresson](TypeExpr) powers Conlang's type checker.
|
||||||
|
//!
|
||||||
|
//! # Syntax
|
||||||
|
//! [`TypeExpr`]` := `[`TupleType`]` | `[`TypePath`]` | `[`Never`]
|
||||||
|
|
||||||
|
pub use super::path::Path as TypePath;
|
||||||
|
|
||||||
|
/// Contains a [Type Expression](self)
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
/// [`TypeExpr`]` := `[`TupleType`]` | `[`TypePath`]` | `[`Empty`]` | `[`Never`]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum TypeExpr {
|
||||||
|
TupleType(TupleType),
|
||||||
|
TypePath(TypePath),
|
||||||
|
Empty(Empty),
|
||||||
|
Never(Never),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [TupleType] represents the [TypeExpr] of a Tuple value
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TupleType {
|
||||||
|
pub types: Vec<TypeExpr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The empty type. You get nothing! You lose!
|
||||||
|
/// # Syntax
|
||||||
|
/// [`Empty`]` := '(' ')'`
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Empty;
|
||||||
|
|
||||||
|
/// The never type. This type can never be constructed, and can only appear if a block of code
|
||||||
|
/// doesn't terminate
|
||||||
|
/// # Syntax
|
||||||
|
/// [`Never`]` := '!'`
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Never;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod expression {
|
pub mod expression {
|
||||||
//! # Expressions
|
//! # Expressions
|
||||||
//!
|
//!
|
||||||
@ -219,17 +257,19 @@ pub mod expression {
|
|||||||
//! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`)
|
//! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`)
|
||||||
//! | 9 | [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */
|
//! | 9 | [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */
|
||||||
//! | 9 | [`Block`] | Block expressions `{` [Expr] `}`
|
//! | 9 | [`Block`] | Block expressions `{` [Expr] `}`
|
||||||
//! | 9 | [`Primary`] | Contains an [Identifier], [Literal](literal::Literal), [Block], [Group], or [Flow](control::Flow)
|
//! | 9 | [`Primary`] | Contains an [Identifier], [Literal], [Block], [Group], or [Flow]
|
||||||
//!
|
//!
|
||||||
//! ## Syntax
|
//! ## Syntax
|
||||||
//! [`Expr`]` := `[`math::Operation`] \
|
//! [`Expr`]` := `[`math::Operation`] \
|
||||||
//! [`Block`]` := '{' `[`Expr`]` '}'` \
|
//! [`Block`]` := '{' `[`Expr`]` '}'` \
|
||||||
//! [`Group`]` := '(' `[`Expr`]`? ')'` \
|
//! [`Group`]` := '(' `[`Expr`]`? ')'` \
|
||||||
//! [`Primary`]` := `[`Identifier`]` | `[`Literal`](literal::Literal)` | `[`Block`]` |
|
//! [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` |
|
||||||
//! `[`Group`]` | `[`control::Flow`]
|
//! `[`Group`]` | `[`Flow`]
|
||||||
//!
|
//!
|
||||||
//! See [control] and [math] for their respective production rules.
|
//! See [control] and [math] for their respective production rules.
|
||||||
use super::{statement::Stmt, *};
|
use super::{literal::Literal, statement::Stmt, *};
|
||||||
|
use control::Flow;
|
||||||
|
use tuple::Group;
|
||||||
|
|
||||||
/// Contains an expression
|
/// Contains an expression
|
||||||
///
|
///
|
||||||
@ -241,18 +281,18 @@ pub mod expression {
|
|||||||
/// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest
|
/// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest
|
||||||
/// derivation)
|
/// derivation)
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Primary`]` := `[`IDENTIFIER`](Identifier)`
|
/// [`Primary`]` := `[`Identifier`]`
|
||||||
/// | `[`Literal`](literal::Literal)`
|
/// | `[`Literal`]`
|
||||||
/// | `[`Block`]`
|
/// | `[`Block`]`
|
||||||
/// | `[`Group`](tuple::Group)`
|
/// | `[`Group`]`
|
||||||
/// | `[`Branch`](control::Flow)
|
/// | `[`Branch`](Flow)
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Primary {
|
pub enum Primary {
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
Literal(literal::Literal),
|
Literal(Literal),
|
||||||
Block(Block),
|
Block(Block),
|
||||||
Group(tuple::Group),
|
Group(Group),
|
||||||
Branch(control::Flow),
|
Branch(Flow),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains a Block Expression
|
/// Contains a Block Expression
|
||||||
@ -260,6 +300,7 @@ pub mod expression {
|
|||||||
/// [`Block`] := `'{'` [`Expr`] `'}'`
|
/// [`Block`] := `'{'` [`Expr`] `'}'`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
|
pub let_count: Option<usize>,
|
||||||
pub statements: Vec<Stmt>,
|
pub statements: Vec<Stmt>,
|
||||||
pub expr: Option<Box<Expr>>,
|
pub expr: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
@ -352,7 +393,7 @@ pub mod expression {
|
|||||||
//! [`Shift`][2]` := `[`Term`][2]` (`[`ShiftOp`][5]` `[`Term`][2]` )*` \
|
//! [`Shift`][2]` := `[`Term`][2]` (`[`ShiftOp`][5]` `[`Term`][2]` )*` \
|
||||||
//! [`Term`][2]` := `[`Factor`][2]` (`[`TermOp`][5]` `[`Factor`][2]` )*` \
|
//! [`Term`][2]` := `[`Factor`][2]` (`[`TermOp`][5]` `[`Factor`][2]` )*` \
|
||||||
//! [`Factor`][2]` := `[`Unary`][1]` (`[`FactorOp`][5]` `[`Unary`][1]` )*` \
|
//! [`Factor`][2]` := `[`Unary`][1]` (`[`FactorOp`][5]` `[`Unary`][1]` )*` \
|
||||||
//! [`Unary`][1]` := (`[`UnaryOp`][4]`)* `[`FnCall`][7]
|
//! [`Unary`][1]` := (`[`UnaryOp`][4]`)* `[`Call`]
|
||||||
//!
|
//!
|
||||||
//! [1]: Operation::Unary
|
//! [1]: Operation::Unary
|
||||||
//! [2]: Operation::Binary
|
//! [2]: Operation::Binary
|
||||||
@ -704,10 +745,10 @@ pub mod expression {
|
|||||||
|
|
||||||
pub mod visitor {
|
pub mod visitor {
|
||||||
//! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super)
|
//! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super)
|
||||||
//!
|
//!
|
||||||
//! This trait is mostly here to ensure that every branch of the tree is accounted for.
|
//! This trait is mostly here to ensure that every branch of the tree is accounted for.
|
||||||
//!
|
//!
|
||||||
//! Default implementations are provided for
|
//! Default implementations are provided for
|
||||||
use super::{
|
use super::{
|
||||||
expression::{call::*, control::*, math::*, tuple::*, Block, *},
|
expression::{call::*, control::*, math::*, tuple::*, Block, *},
|
||||||
literal::*,
|
literal::*,
|
||||||
@ -716,6 +757,7 @@ pub mod visitor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super)
|
/// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super)
|
||||||
|
#[deprecated]
|
||||||
pub trait Visitor<R> {
|
pub trait Visitor<R> {
|
||||||
/// Visit the start of an AST
|
/// Visit the start of an AST
|
||||||
fn visit(&mut self, start: &Start) -> R {
|
fn visit(&mut self, start: &Start) -> R {
|
||||||
@ -733,9 +775,9 @@ pub mod visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Visit a [Let statement](Let)
|
/// Visit a [Let statement](Let)
|
||||||
fn visit_let(&mut self, stmt: &Let) -> R;
|
fn visit_let(&mut self, decl: &Let) -> R;
|
||||||
/// Visit a [Fn declaration](FnDecl)
|
/// Visit a [Fn declaration](FnDecl)
|
||||||
fn visit_fn_decl(&mut self, function: &FnDecl) -> R;
|
fn visit_fn_decl(&mut self, decl: &FnDecl) -> R;
|
||||||
|
|
||||||
/// Visit an [Expression](Expr)
|
/// Visit an [Expression](Expr)
|
||||||
fn visit_expr(&mut self, expr: &Expr) -> R {
|
fn visit_expr(&mut self, expr: &Expr) -> R {
|
||||||
@ -781,10 +823,11 @@ pub mod visitor {
|
|||||||
/// Visit a [Unary] Operation
|
/// Visit a [Unary] Operation
|
||||||
fn visit_unary(&mut self, unary: &Unary) -> R;
|
fn visit_unary(&mut self, unary: &Unary) -> R;
|
||||||
// Math operators
|
// Math operators
|
||||||
|
/// Visit an [Assignment](Assign) [operator](operator::Assign)
|
||||||
fn visit_assign_op(&mut self, op: &operator::Assign) -> R;
|
fn visit_assign_op(&mut self, op: &operator::Assign) -> R;
|
||||||
/// Visit a [Binary](Operation::Binary) [operator](operator::Binary)
|
/// Visit a [Binary] [operator](operator::Binary)
|
||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> R;
|
fn visit_binary_op(&mut self, op: &operator::Binary) -> R;
|
||||||
/// Visit a [Unary](Operation::Unary) [operator](operator::Unary)
|
/// Visit a [Unary] [operator](operator::Unary)
|
||||||
fn visit_unary_op(&mut self, op: &operator::Unary) -> R;
|
fn visit_unary_op(&mut self, op: &operator::Unary) -> R;
|
||||||
|
|
||||||
/// Visit a [Primary] expression
|
/// Visit a [Primary] expression
|
||||||
@ -796,7 +839,7 @@ pub mod visitor {
|
|||||||
Primary::Literal(v) => self.visit_literal(v),
|
Primary::Literal(v) => self.visit_literal(v),
|
||||||
Primary::Block(v) => self.visit_block(v),
|
Primary::Block(v) => self.visit_block(v),
|
||||||
Primary::Group(v) => self.visit_group(v),
|
Primary::Group(v) => self.visit_group(v),
|
||||||
Primary::Branch(v) => self.visit_branch_expr(v),
|
Primary::Branch(v) => self.visit_branch(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,8 +847,8 @@ pub mod visitor {
|
|||||||
///
|
///
|
||||||
/// [`Flow`]` := `[`While`]` | `[`If`]` | `[`For`]`
|
/// [`Flow`]` := `[`While`]` | `[`If`]` | `[`For`]`
|
||||||
/// | `[`Continue`]` | `[`Return`]` | `[`Break`]
|
/// | `[`Continue`]` | `[`Return`]` | `[`Break`]
|
||||||
fn visit_branch_expr(&mut self, expr: &Flow) -> R {
|
fn visit_branch(&mut self, flow: &Flow) -> R {
|
||||||
match expr {
|
match flow {
|
||||||
Flow::While(e) => self.visit_while(e),
|
Flow::While(e) => self.visit_while(e),
|
||||||
Flow::If(e) => self.visit_if(e),
|
Flow::If(e) => self.visit_if(e),
|
||||||
Flow::For(e) => self.visit_for(e),
|
Flow::For(e) => self.visit_for(e),
|
||||||
@ -859,3 +902,46 @@ pub mod visitor {
|
|||||||
fn visit_empty(&mut self) -> R;
|
fn visit_empty(&mut self) -> R;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod todo {
|
||||||
|
//! temporary storage for pending expression work. \
|
||||||
|
//! when an item is in progress, remove it from todo.
|
||||||
|
//!
|
||||||
|
//! # General TODOs:
|
||||||
|
//! - [ ] REMOVE VISITOR TRAIT
|
||||||
|
//! - [ ]
|
||||||
|
//! - [x] Implement support for storing items in the AST
|
||||||
|
//! - [ ] Keep track of the source location of each node
|
||||||
|
//! - [ ] Implement paths
|
||||||
|
//! - [x] Implement functions
|
||||||
|
//! - [ ] Implement structs
|
||||||
|
//! - [ ] Implement enums
|
||||||
|
//! - [ ] Implement implementation
|
||||||
|
//! - [ ] Store token spans in AST
|
||||||
|
pub mod path {
|
||||||
|
//! Path support
|
||||||
|
//! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | `foo::bar::*`)
|
||||||
|
//!
|
||||||
|
//! Path resolution will be vital to the implementation of structs, enums, impl blocks,
|
||||||
|
//! traits, modules, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod structure {
|
||||||
|
//! Struct support
|
||||||
|
//! - [ ] Add struct declaration expression (returns a struct declaration)
|
||||||
|
//! - [ ] Add struct value expression (returns a struct value)
|
||||||
|
//! - [ ] Add struct update syntax (yippee!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod enumeration {
|
||||||
|
//! Enum support
|
||||||
|
//! - [ ] Add enum declaration expression (returns an enum declaration)
|
||||||
|
//! - [ ] Add enum value expression (returns an enum value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod implementation {
|
||||||
|
//! Impl block support
|
||||||
|
//! - [ ] Add impl block expression? Statement?
|
||||||
|
//! - [ ] Add member function call expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Interprets an AST as a program
|
//! Interprets an AST as a program
|
||||||
|
#![allow(deprecated)] // TODO: REMOVE
|
||||||
|
|
||||||
use crate::ast::preamble::*;
|
use crate::ast::preamble::*;
|
||||||
use error::{Error, IResult};
|
use error::{Error, IResult};
|
||||||
@ -9,7 +10,7 @@ use temp_type_impl::ConValue;
|
|||||||
pub trait Callable: std::fmt::Debug {
|
pub trait Callable: std::fmt::Debug {
|
||||||
/// Calls this [Callable] in the provided [Interpreter], with [ConValue] args \
|
/// Calls this [Callable] in the provided [Interpreter], with [ConValue] args \
|
||||||
/// The Callable is responsible for checking the argument count and validating types
|
/// The Callable is responsible for checking the argument count and validating types
|
||||||
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()>;
|
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue>;
|
||||||
/// Returns the common name of this identifier.
|
/// Returns the common name of this identifier.
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
}
|
}
|
||||||
@ -105,7 +106,7 @@ pub mod temp_type_impl {
|
|||||||
_ => "",
|
_ => "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()> {
|
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(func) => func.call(interpreter, args),
|
Self::Function(func) => func.call(interpreter, args),
|
||||||
Self::BuiltIn(func) => func.call(interpreter, args),
|
Self::BuiltIn(func) => func.call(interpreter, args),
|
||||||
@ -347,7 +348,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_let(&mut self, stmt: &Let) -> IResult<()> {
|
fn visit_let(&mut self, stmt: &Let) -> IResult<()> {
|
||||||
let Let { name: Identifier(name), init, .. } = stmt;
|
let Let { name: Name { name: Identifier { name, .. }, .. }, init, .. } = stmt;
|
||||||
if let Some(init) = init {
|
if let Some(init) = init {
|
||||||
self.visit_expr(init)?;
|
self.visit_expr(init)?;
|
||||||
let init = self.pop()?;
|
let init = self.pop()?;
|
||||||
@ -394,7 +395,8 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
let (ConValue::Tuple(args), callee) = self.pop_two()? else {
|
let (ConValue::Tuple(args), callee) = self.pop_two()? else {
|
||||||
Err(Error::TypeError)?
|
Err(Error::TypeError)?
|
||||||
};
|
};
|
||||||
callee.call(self, &args)?;
|
let return_value = callee.call(self, &args)?;
|
||||||
|
self.push(return_value);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -404,7 +406,8 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
let math::Assign { target, operator, init } = assign;
|
let math::Assign { target, operator, init } = assign;
|
||||||
self.visit_operation(init)?;
|
self.visit_operation(init)?;
|
||||||
let init = self.pop()?;
|
let init = self.pop()?;
|
||||||
let resolved = self.scope.get_mut(&target.0)?;
|
let resolved = self.scope.get_mut(&target.name)?;
|
||||||
|
|
||||||
if let Assign::Assign = operator {
|
if let Assign::Assign = operator {
|
||||||
use std::mem::discriminant as variant;
|
use std::mem::discriminant as variant;
|
||||||
// runtime typecheck
|
// runtime typecheck
|
||||||
@ -418,9 +421,11 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
self.push(ConValue::Empty);
|
self.push(ConValue::Empty);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(target) = resolved.as_mut() else {
|
let Some(target) = resolved.as_mut() else {
|
||||||
Err(Error::NotInitialized(target.0.to_owned()))?
|
Err(Error::NotInitialized(target.name.to_owned()))?
|
||||||
};
|
};
|
||||||
|
|
||||||
match operator {
|
match operator {
|
||||||
Assign::AddAssign => target.add_assign(init)?,
|
Assign::AddAssign => target.add_assign(init)?,
|
||||||
Assign::SubAssign => target.sub_assign(init)?,
|
Assign::SubAssign => target.sub_assign(init)?,
|
||||||
@ -434,12 +439,13 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Assign::ShrAssign => target.shr_assign(init)?,
|
Assign::ShrAssign => target.shr_assign(init)?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push(ConValue::Empty);
|
self.push(ConValue::Empty);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> {
|
fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> {
|
||||||
use math::Binary;
|
|
||||||
let Binary { first, other } = bin;
|
let Binary { first, other } = bin;
|
||||||
|
|
||||||
self.visit_operation(first)?;
|
self.visit_operation(first)?;
|
||||||
@ -469,15 +475,19 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> {
|
fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> {
|
||||||
let math::Unary { operand, operators } = unary;
|
let Unary { operand, operators } = unary;
|
||||||
|
|
||||||
self.visit_operation(operand)?;
|
self.visit_operation(operand)?;
|
||||||
|
|
||||||
for op in operators.iter().rev() {
|
for op in operators.iter().rev() {
|
||||||
self.visit_unary_op(op)?;
|
self.visit_unary_op(op)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,6 +498,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> {
|
fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> {
|
||||||
use operator::Binary;
|
use operator::Binary;
|
||||||
let (second, first) = self.pop_two()?;
|
let (second, first) = self.pop_two()?;
|
||||||
|
|
||||||
let out = match op {
|
let out = match op {
|
||||||
Binary::Mul => first * second,
|
Binary::Mul => first * second,
|
||||||
Binary::Div => first / second,
|
Binary::Div => first / second,
|
||||||
@ -511,12 +522,14 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Binary::GreaterEq => first.gt_eq(&second),
|
Binary::GreaterEq => first.gt_eq(&second),
|
||||||
Binary::Greater => first.gt(&second),
|
Binary::Greater => first.gt(&second),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
self.push(out);
|
self.push(out);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> {
|
fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> {
|
||||||
let operand = self.pop()?;
|
let operand = self.pop()?;
|
||||||
|
|
||||||
self.push(match op {
|
self.push(match op {
|
||||||
operator::Unary::RefRef => todo!(),
|
operator::Unary::RefRef => todo!(),
|
||||||
operator::Unary::Ref => todo!(),
|
operator::Unary::Ref => todo!(),
|
||||||
@ -530,10 +543,11 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
operator::Unary::Tilde => todo!(),
|
operator::Unary::Tilde => todo!(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_if(&mut self, expr: &control::If) -> IResult<()> {
|
fn visit_if(&mut self, expr: &If) -> IResult<()> {
|
||||||
self.visit_expr(&expr.cond)?;
|
self.visit_expr(&expr.cond)?;
|
||||||
if self.pop()?.truthy()? {
|
if self.pop()?.truthy()? {
|
||||||
self.visit_block(&expr.body)?;
|
self.visit_block(&expr.body)?;
|
||||||
@ -545,7 +559,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_while(&mut self, expr: &control::While) -> IResult<()> {
|
fn visit_while(&mut self, expr: &While) -> IResult<()> {
|
||||||
while {
|
while {
|
||||||
self.visit_expr(&expr.cond)?;
|
self.visit_expr(&expr.cond)?;
|
||||||
self.pop()?.truthy()?
|
self.pop()?.truthy()?
|
||||||
@ -581,7 +595,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
_ => Err(Error::NotIterable)?,
|
_ => Err(Error::NotIterable)?,
|
||||||
};
|
};
|
||||||
for loop_var in bounds.0..=bounds.1 {
|
for loop_var in bounds.0..=bounds.1 {
|
||||||
self.scope.insert(&expr.var.0, Some(loop_var.into()));
|
self.scope.insert(&expr.var.name, Some(loop_var.into()));
|
||||||
let Err(out) = self.visit_block(&expr.body) else {
|
let Err(out) = self.visit_block(&expr.body) else {
|
||||||
self.pop()?;
|
self.pop()?;
|
||||||
continue;
|
continue;
|
||||||
@ -627,7 +641,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> {
|
fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> {
|
||||||
let value = self.resolve(&ident.0)?;
|
let value = self.resolve(&ident.name)?;
|
||||||
self.push(value);
|
self.push(value);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -670,8 +684,17 @@ impl Default for Interpreter {
|
|||||||
|
|
||||||
pub mod function {
|
pub mod function {
|
||||||
//! Represents a block of code which lives inside the Interpreter
|
//! Represents a block of code which lives inside the Interpreter
|
||||||
use super::{Callable, ConValue, Error, FnDecl, IResult, Identifier, Interpreter};
|
use super::{
|
||||||
use crate::ast::visitor::Visitor;
|
// scope::Environment,
|
||||||
|
Callable,
|
||||||
|
ConValue,
|
||||||
|
Error,
|
||||||
|
FnDecl,
|
||||||
|
IResult,
|
||||||
|
Identifier,
|
||||||
|
Interpreter,
|
||||||
|
};
|
||||||
|
use crate::ast::{preamble::Name, visitor::Visitor};
|
||||||
/// Represents a block of code which persists inside the Interpreter
|
/// Represents a block of code which persists inside the Interpreter
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
@ -679,19 +702,23 @@ pub mod function {
|
|||||||
declaration: Box<FnDecl>,
|
declaration: Box<FnDecl>,
|
||||||
// /// Stores the enclosing scope of the function
|
// /// Stores the enclosing scope of the function
|
||||||
// TODO: Capture variables
|
// TODO: Capture variables
|
||||||
|
//environment: Box<Environment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(declaration: &FnDecl) -> Self {
|
pub fn new(declaration: &FnDecl) -> Self {
|
||||||
Self { declaration: declaration.clone().into() }
|
Self {
|
||||||
|
declaration: declaration.clone().into(),
|
||||||
|
//environment: Box::new(Environment::new()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Callable for Function {
|
impl Callable for Function {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.declaration.name.0
|
&self.declaration.name.name.name
|
||||||
}
|
}
|
||||||
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<()> {
|
fn call(&self, interpreter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
// Check arg mapping
|
// Check arg mapping
|
||||||
if args.len() != self.declaration.args.len() {
|
if args.len() != self.declaration.args.len() {
|
||||||
return Err(Error::ArgNumber {
|
return Err(Error::ArgNumber {
|
||||||
@ -701,17 +728,19 @@ pub mod function {
|
|||||||
}
|
}
|
||||||
// TODO: Isolate cross-function scopes!
|
// TODO: Isolate cross-function scopes!
|
||||||
interpreter.scope.enter();
|
interpreter.scope.enter();
|
||||||
for (Identifier(arg), value) in self.declaration.args.iter().zip(args) {
|
for (Name { name: Identifier { name, .. }, .. }, value) in
|
||||||
interpreter.scope.insert(arg, Some(value.clone()));
|
self.declaration.args.iter().zip(args)
|
||||||
|
{
|
||||||
|
interpreter.scope.insert(name, Some(value.clone()));
|
||||||
}
|
}
|
||||||
match interpreter.visit_block(&self.declaration.body) {
|
match interpreter.visit_block(&self.declaration.body) {
|
||||||
Err(Error::Return(value)) => interpreter.push(value),
|
Err(Error::Return(value)) => interpreter.push(value),
|
||||||
Err(Error::Break(value)) => Err(Error::BadBreak(value))?,
|
Err(Error::Break(value)) => Err(Error::BadBreak(value))?,
|
||||||
Err(e) => Err(e)?,
|
Err(e) => Err(e)?,
|
||||||
Ok(_) => (),
|
Ok(()) => (),
|
||||||
}
|
}
|
||||||
interpreter.scope.exit()?;
|
interpreter.scope.exit()?;
|
||||||
Ok(())
|
interpreter.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -719,14 +748,12 @@ pub mod function {
|
|||||||
pub mod builtin {
|
pub mod builtin {
|
||||||
mod builtin_imports {
|
mod builtin_imports {
|
||||||
pub use crate::interpreter::{
|
pub use crate::interpreter::{
|
||||||
error::{Error, IResult},
|
error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, Interpreter,
|
||||||
temp_type_impl::ConValue,
|
|
||||||
BuiltIn, Callable, Interpreter,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
use super::BuiltIn;
|
use super::BuiltIn;
|
||||||
/// Builtins to load when a new interpreter is created
|
/// Builtins to load when a new interpreter is created
|
||||||
pub const DEFAULT_BUILTINS: &[&dyn BuiltIn] = &[&print::Print, &dbg::Dbg];
|
pub const DEFAULT_BUILTINS: &[&dyn BuiltIn] = &[&print::Print, &dbg::Dbg, &dump::Dump];
|
||||||
|
|
||||||
mod print {
|
mod print {
|
||||||
//! Implements the unstable `print(...)` builtin
|
//! Implements the unstable `print(...)` builtin
|
||||||
@ -738,11 +765,12 @@ pub mod builtin {
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl Callable for Print {
|
impl Callable for Print {
|
||||||
fn name(&self) -> &'static str { "print" }
|
fn name(&self) -> &'static str { "print" }
|
||||||
fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> {
|
fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
for arg in args { print!("{arg}") }
|
for arg in args {
|
||||||
|
print!("{arg}")
|
||||||
|
}
|
||||||
println!();
|
println!();
|
||||||
inter.push(ConValue::Empty);
|
Ok(ConValue::Empty)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,10 +783,27 @@ pub mod builtin {
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl Callable for Dbg {
|
impl Callable for Dbg {
|
||||||
fn name(&self) -> &str { "dbg" }
|
fn name(&self) -> &str { "dbg" }
|
||||||
fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> {
|
fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
println!("{args:?}");
|
println!("{args:?}");
|
||||||
inter.push(args);
|
Ok(args.into())
|
||||||
Ok(())
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod dump {
|
||||||
|
use super::builtin_imports::*;
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Dump;
|
||||||
|
impl BuiltIn for Dump {}
|
||||||
|
impl Callable for Dump {
|
||||||
|
fn call(&self, interpreter: &mut Interpreter, _args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
println!("Scope:\n{}", interpreter.scope);
|
||||||
|
println!("Stack:{:#?}", interpreter.stack);
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"dump"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -774,7 +819,7 @@ pub mod scope {
|
|||||||
temp_type_impl::ConValue,
|
temp_type_impl::ConValue,
|
||||||
FnDecl,
|
FnDecl,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt::Display};
|
||||||
|
|
||||||
/// Implements a nested lexical scope
|
/// Implements a nested lexical scope
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@ -783,6 +828,19 @@ pub mod scope {
|
|||||||
vars: HashMap<String, Option<ConValue>>,
|
vars: HashMap<String, Option<ConValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Environment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for (var, val) in &self.vars {
|
||||||
|
writeln!(f, "{var}: {}", if val.is_some() { "..." } else { "None" })?;
|
||||||
|
}
|
||||||
|
"--- Frame ---\n".fmt(f)?;
|
||||||
|
if let Some(outer) = &self.outer {
|
||||||
|
outer.fmt(f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut out = Self::default();
|
let mut out = Self::default();
|
||||||
@ -835,8 +893,10 @@ pub mod scope {
|
|||||||
}
|
}
|
||||||
/// A convenience function for registering a [FnDecl] as a [Function]
|
/// A convenience function for registering a [FnDecl] as a [Function]
|
||||||
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
||||||
self.vars
|
self.vars.insert(
|
||||||
.insert(decl.name.0.clone(), Some(Function::new(decl).into()));
|
decl.name.name.name.clone(),
|
||||||
|
Some(Function::new(decl).into()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,37 @@
|
|||||||
//! Parses [tokens](super::token) into an [AST](super::ast)
|
//! Parses [tokens](super::token) into an [AST](super::ast)
|
||||||
|
|
||||||
use super::{ast::preamble::*, lexer::Lexer, token::preamble::*};
|
use super::{ast::preamble::*, lexer::Lexer, token::preamble::*};
|
||||||
use error::{Error, Reason::*, *};
|
use error::{Error, *};
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
use super::{Token, Type};
|
use super::{Token, Type};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub trait WrapError {
|
||||||
|
/// Wraps this error in a parent [Error]
|
||||||
|
fn wrap(self, parent: Error) -> Self;
|
||||||
|
}
|
||||||
|
impl WrapError for Error {
|
||||||
|
fn wrap(self, parent: Error) -> Self {
|
||||||
|
Self { child: Some(self.into()), ..parent }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> WrapError for Result<T, Error> {
|
||||||
|
fn wrap(self, parent: Error) -> Self {
|
||||||
|
self.map_err(|e| e.wrap(parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The reason for the [Error]
|
/// The reason for the [Error]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub enum Reason {
|
pub enum Reason {
|
||||||
Expected(Type),
|
Expected(Type),
|
||||||
Unexpected(Type),
|
Unexpected(Type),
|
||||||
|
NotPathSegment(Type),
|
||||||
NotIdentifier,
|
NotIdentifier,
|
||||||
|
NotStatement,
|
||||||
|
NotLet,
|
||||||
|
NotFnDecl,
|
||||||
NotOperator,
|
NotOperator,
|
||||||
NotLiteral,
|
NotLiteral,
|
||||||
NotString,
|
NotString,
|
||||||
@ -37,7 +56,11 @@ pub mod error {
|
|||||||
match self {
|
match self {
|
||||||
Self::Expected(t) => write!(f, "Expected {t}"),
|
Self::Expected(t) => write!(f, "Expected {t}"),
|
||||||
Self::Unexpected(t) => write!(f, "Unexpected {t} in bagging area"),
|
Self::Unexpected(t) => write!(f, "Unexpected {t} in bagging area"),
|
||||||
|
Self::NotPathSegment(t) => write!(f, "{t} not a path segment"),
|
||||||
Self::NotIdentifier => "Not an identifier".fmt(f),
|
Self::NotIdentifier => "Not an identifier".fmt(f),
|
||||||
|
Self::NotStatement => "Not a statement".fmt(f),
|
||||||
|
Self::NotLet => "Not a let statement".fmt(f),
|
||||||
|
Self::NotFnDecl => "Not a valid function declaration".fmt(f),
|
||||||
Self::NotOperator => "Not an operator".fmt(f),
|
Self::NotOperator => "Not an operator".fmt(f),
|
||||||
Self::NotLiteral => "Not a literal".fmt(f),
|
Self::NotLiteral => "Not a literal".fmt(f),
|
||||||
Self::NotString => "Not a string".fmt(f),
|
Self::NotString => "Not a string".fmt(f),
|
||||||
@ -67,11 +90,15 @@ pub mod error {
|
|||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
reason: Reason,
|
reason: Reason,
|
||||||
|
child: Option<Box<Self>>,
|
||||||
start: Option<Token>,
|
start: Option<Token>,
|
||||||
}
|
}
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(child) = &self.child {
|
||||||
|
write!(f, "{child}: ")?;
|
||||||
|
}
|
||||||
if let Some(token) = &self.start {
|
if let Some(token) = &self.start {
|
||||||
write!(f, "{}:{}: ", token.line(), token.col())?;
|
write!(f, "{}:{}: ", token.line(), token.col())?;
|
||||||
}
|
}
|
||||||
@ -84,7 +111,7 @@ pub mod error {
|
|||||||
#[doc = concat!("[`", stringify!($reason), "`]")]
|
#[doc = concat!("[`", stringify!($reason), "`]")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn $fn($($($p : $t),*)?) -> Self {
|
pub(crate) fn $fn($($($p : $t),*)?) -> Self {
|
||||||
Self { reason: $reason$(($($p)*))?, start: None }
|
Self { reason: $reason$(($($p)*))?, child: None, start: None }
|
||||||
}
|
}
|
||||||
)*}
|
)*}
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -104,14 +131,14 @@ pub mod error {
|
|||||||
pub fn reason(&self) -> Reason {
|
pub fn reason(&self) -> Reason {
|
||||||
self.reason
|
self.reason
|
||||||
}
|
}
|
||||||
/// Modifies the [Reason] of this error
|
|
||||||
pub fn with_reason(self, reason: Reason) -> Self {
|
|
||||||
Self { reason, ..self }
|
|
||||||
}
|
|
||||||
error_impl! {
|
error_impl! {
|
||||||
expected(e: Type): Expected,
|
expected(e: Type): Expected,
|
||||||
unexpected(e: Type): Unexpected,
|
unexpected(e: Type): Unexpected,
|
||||||
|
not_path_segment(e: Type): NotPathSegment,
|
||||||
not_identifier: NotIdentifier,
|
not_identifier: NotIdentifier,
|
||||||
|
not_statement: NotStatement,
|
||||||
|
not_let: NotLet,
|
||||||
|
not_fn_decl: NotFnDecl,
|
||||||
not_operator: NotOperator,
|
not_operator: NotOperator,
|
||||||
not_literal: NotLiteral,
|
not_literal: NotLiteral,
|
||||||
not_string: NotString,
|
not_string: NotString,
|
||||||
@ -160,20 +187,25 @@ impl Parser {
|
|||||||
pub fn new(tokens: Vec<Token>) -> Self {
|
pub fn new(tokens: Vec<Token>) -> Self {
|
||||||
Self { tokens, panic_stack: vec![], errors: vec![], cursor: 0 }
|
Self { tokens, panic_stack: vec![], errors: vec![], cursor: 0 }
|
||||||
}
|
}
|
||||||
|
/// Resets the parser, so it can be reused
|
||||||
|
pub fn reset(&mut self) -> &mut Self {
|
||||||
|
*self = Self::new(std::mem::take(&mut self.tokens));
|
||||||
|
self
|
||||||
|
}
|
||||||
/// Parses the [start of an AST](Start)
|
/// Parses the [start of an AST](Start)
|
||||||
pub fn parse(&mut self) -> PResult<Start> {
|
pub fn parse(&mut self) -> PResult<Start> {
|
||||||
self.consume_comments();
|
self.consume_comments();
|
||||||
Ok(Start(self.program()?))
|
Ok(Start(self.program()?))
|
||||||
}
|
}
|
||||||
/// Parses only one expression
|
/// Parses only one expression
|
||||||
pub fn parse_expr(&mut self) -> PResult<expression::Expr> {
|
pub fn parse_expr(&mut self) -> PResult<Expr> {
|
||||||
self.expr()
|
self.expr()
|
||||||
}
|
}
|
||||||
/// Peeks at the current token
|
/// Peeks at the current token
|
||||||
pub fn peek(&self) -> PResult<&Token> {
|
pub fn peek(&self) -> PResult<&Token> {
|
||||||
self.tokens
|
self.tokens
|
||||||
.get(self.cursor)
|
.get(self.cursor)
|
||||||
.ok_or(Error::end_of_file().maybe_token(self.tokens.last().cloned()))
|
.ok_or_else(|| Error::end_of_file().maybe_token(self.tokens.last().cloned()))
|
||||||
}
|
}
|
||||||
/// Consumes any number of consecutive comments
|
/// Consumes any number of consecutive comments
|
||||||
fn consume_comments(&mut self) -> &mut Self {
|
fn consume_comments(&mut self) -> &mut Self {
|
||||||
@ -212,9 +244,7 @@ impl Parser {
|
|||||||
/// Advances forward until a token with type [`t`](Type) is encountered
|
/// Advances forward until a token with type [`t`](Type) is encountered
|
||||||
fn advance_until(&mut self, t: Type) -> PResult<&mut Self> {
|
fn advance_until(&mut self, t: Type) -> PResult<&mut Self> {
|
||||||
while self.matches(t).is_err() {
|
while self.matches(t).is_err() {
|
||||||
self.check_eof()
|
self.check_eof().wrap(Error::expected(t))?.consume();
|
||||||
.map_err(|e| e.with_reason(Expected(t)))?
|
|
||||||
.consume();
|
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@ -244,9 +274,10 @@ impl Parser {
|
|||||||
fn matches(&mut self, t: Type) -> PResult<&Token> {
|
fn matches(&mut self, t: Type) -> PResult<&Token> {
|
||||||
let token = self.check_eof()?.peek().expect("self should not be eof");
|
let token = self.check_eof()?.peek().expect("self should not be eof");
|
||||||
if token.ty() != t {
|
if token.ty() != t {
|
||||||
Err(Error::expected(t).token(token.clone()))?
|
Err(Error::expected(t).token(token.clone()))
|
||||||
|
} else {
|
||||||
|
Ok(token)
|
||||||
}
|
}
|
||||||
Ok(token)
|
|
||||||
}
|
}
|
||||||
/// Consumes, without returning, a token with the given [Keyword], or returns an error.
|
/// Consumes, without returning, a token with the given [Keyword], or returns an error.
|
||||||
///
|
///
|
||||||
@ -282,7 +313,7 @@ impl Parser {
|
|||||||
/// Parses an [Identifier]
|
/// Parses an [Identifier]
|
||||||
fn identifier(&mut self) -> PResult<Identifier> {
|
fn identifier(&mut self) -> PResult<Identifier> {
|
||||||
let out = match self.matches(Type::Identifier)?.data() {
|
let out = match self.matches(Type::Identifier)?.data() {
|
||||||
Data::Identifier(id) => Identifier(id.to_string()),
|
Data::Identifier(id) => Identifier { name: id.to_string(), index: None },
|
||||||
_ => Err(Error::not_identifier())?,
|
_ => Err(Error::not_identifier())?,
|
||||||
};
|
};
|
||||||
self.consume();
|
self.consume();
|
||||||
@ -363,30 +394,25 @@ impl Parser {
|
|||||||
fn stmt(&mut self) -> PResult<Stmt> {
|
fn stmt(&mut self) -> PResult<Stmt> {
|
||||||
let token = self.peek()?;
|
let token = self.peek()?;
|
||||||
match token.ty() {
|
match token.ty() {
|
||||||
Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let),
|
Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let).wrap(Error::not_let()),
|
||||||
Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn),
|
Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn).wrap(Error::not_fn_decl()),
|
||||||
_ => {
|
_ => {
|
||||||
let out = Stmt::Expr(self.expr()?);
|
let out = Stmt::Expr(self.expr()?);
|
||||||
self.consume_type(Type::Semi)?;
|
self.consume_type(Type::Semi)?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.wrap(Error::not_statement())
|
||||||
}
|
}
|
||||||
/// Parses a [Let] statement
|
/// Parses a [Let] statement
|
||||||
fn let_stmt(&mut self) -> PResult<Let> {
|
fn let_stmt(&mut self) -> PResult<Let> {
|
||||||
let out = Let {
|
self.keyword(Keyword::Let)?;
|
||||||
mutable: self.consume().keyword(Keyword::Mut).is_ok(),
|
let out =
|
||||||
name: self.identifier()?,
|
Let { name: self.name()?, init: self.consume_type(Type::Eq).and_then(Self::expr).ok() };
|
||||||
ty: self
|
|
||||||
.consume_type(Type::Colon)
|
|
||||||
.and_then(Self::identifier)
|
|
||||||
.ok(),
|
|
||||||
init: self.consume_type(Type::Eq).and_then(Self::expr).ok(),
|
|
||||||
};
|
|
||||||
self.consume_type(Type::Semi)?;
|
self.consume_type(Type::Semi)?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
/// Parses a [Function] statement
|
/// Parses a [function declaration](FnDecl) statement
|
||||||
fn fn_decl(&mut self) -> PResult<FnDecl> {
|
fn fn_decl(&mut self) -> PResult<FnDecl> {
|
||||||
self.keyword(Keyword::Fn)?;
|
self.keyword(Keyword::Fn)?;
|
||||||
let name = self.identifier()?;
|
let name = self.identifier()?;
|
||||||
@ -394,37 +420,98 @@ impl Parser {
|
|||||||
let args = self.params()?;
|
let args = self.params()?;
|
||||||
self.consume_type(Type::RParen)?;
|
self.consume_type(Type::RParen)?;
|
||||||
// TODO: Parse type-expressions and store return types in the AST
|
// TODO: Parse type-expressions and store return types in the AST
|
||||||
if self.consume_type(Type::Arrow).is_ok() {
|
let ty = if self.consume_type(Type::Arrow).is_ok() {
|
||||||
self.expr()?;
|
Some(self.type_expr()?)
|
||||||
}
|
} else {
|
||||||
Ok(FnDecl { name, args, body: self.block()? })
|
None
|
||||||
|
};
|
||||||
|
Ok(FnDecl { name: Name { name, mutable: false, ty }, args, body: self.block()? })
|
||||||
}
|
}
|
||||||
|
/// Parses a [parameter](Name) list for [FnDecl]
|
||||||
fn params(&mut self) -> PResult<Vec<Identifier>> {
|
fn params(&mut self) -> PResult<Vec<Name>> {
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
while let Ok(ident) = self.identifier() {
|
while let Ok(name) = self.name() {
|
||||||
args.push(ident);
|
args.push(name);
|
||||||
if self.consume_type(Type::Colon).is_ok() {
|
|
||||||
// TODO: Parse type-expressions and make this mandatory
|
|
||||||
self.expr()?;
|
|
||||||
}
|
|
||||||
if self.consume_type(Type::Comma).is_err() {
|
if self.consume_type(Type::Comma).is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(args)
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
/// Parses a [Name]; the object of a let statement, or a single function parameter.
|
||||||
|
fn name(&mut self) -> PResult<Name> {
|
||||||
|
Ok(Name {
|
||||||
|
mutable: self.keyword(Keyword::Mut).is_ok(),
|
||||||
|
name: self.identifier()?,
|
||||||
|
ty: self
|
||||||
|
.consume_type(Type::Colon)
|
||||||
|
.and_then(|this| this.type_expr())
|
||||||
|
.ok(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/// Path Expressions
|
||||||
|
impl Parser {
|
||||||
|
fn path(&mut self) -> PResult<path::Path> {
|
||||||
|
let absolute = self.consume_type(Type::ColonColon).is_ok();
|
||||||
|
let mut parts = vec![];
|
||||||
|
while let Ok(id) = self.path_part() {
|
||||||
|
parts.push(id);
|
||||||
|
if self.consume_type(Type::ColonColon).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Path { absolute, parts })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_part(&mut self) -> PResult<PathPart> {
|
||||||
|
match self.peek()?.ty() {
|
||||||
|
Type::Identifier => self.identifier().map(PathPart::PathIdent),
|
||||||
|
Type::Keyword(Keyword::Super) => {
|
||||||
|
self.keyword(Keyword::Super).map(|_| PathPart::PathSuper)
|
||||||
|
}
|
||||||
|
Type::Keyword(Keyword::SelfKw) => {
|
||||||
|
self.keyword(Keyword::SelfKw).map(|_| PathPart::PathSelf)
|
||||||
|
}
|
||||||
|
e => Err(Error::not_path_segment(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Type Expressions
|
||||||
|
impl Parser {
|
||||||
|
/// Parses a [Type Expression](TypeExpr)
|
||||||
|
fn type_expr(&mut self) -> PResult<TypeExpr> {
|
||||||
|
match self.peek()?.ty() {
|
||||||
|
Type::LParen => self.type_tuple().map(TypeExpr::TupleType),
|
||||||
|
Type::Bang => self.type_never().map(TypeExpr::Never),
|
||||||
|
_ => self.path().map(TypeExpr::TypePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn type_tuple(&mut self) -> PResult<TupleType> {
|
||||||
|
self.consume_type(Type::LParen)?;
|
||||||
|
let mut types = vec![];
|
||||||
|
while let Ok(ty) = self.type_expr() {
|
||||||
|
types.push(ty);
|
||||||
|
if self.consume_type(Type::Comma).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.consume_type(Type::RParen)?;
|
||||||
|
Ok(TupleType { types })
|
||||||
|
}
|
||||||
|
fn type_never(&mut self) -> PResult<Never> {
|
||||||
|
self.consume_type(Type::Bang).map(|_| Never)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Expressions
|
/// Expressions
|
||||||
impl Parser {
|
impl Parser {
|
||||||
/// Parses an [expression](expression::Expr)
|
/// Parses an [expression](Expr)
|
||||||
fn expr(&mut self) -> PResult<expression::Expr> {
|
fn expr(&mut self) -> PResult<Expr> {
|
||||||
use expression::Expr;
|
|
||||||
Ok(Expr(self.assign()?))
|
Ok(Expr(self.assign()?))
|
||||||
}
|
}
|
||||||
/// Parses a [block expression](expression::Block)
|
/// Parses a [block expression](Block)
|
||||||
fn block(&mut self) -> PResult<expression::Block> {
|
fn block(&mut self) -> PResult<Block> {
|
||||||
use expression::{Block, Expr};
|
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
let mut expr: Option<Box<Expr>> = None;
|
let mut expr: Option<Box<Expr>> = None;
|
||||||
self.consume_type(Type::LCurly)?;
|
self.consume_type(Type::LCurly)?;
|
||||||
@ -440,11 +527,10 @@ impl Parser {
|
|||||||
Err(_) => statements.push(self.stmt()?),
|
Err(_) => statements.push(self.stmt()?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Block { statements, expr })
|
Ok(Block { statements, expr, let_count: None })
|
||||||
}
|
}
|
||||||
/// Parses a [primary expression](expression::Primary)
|
/// Parses a [primary expression](Primary)
|
||||||
fn primary(&mut self) -> PResult<expression::Primary> {
|
fn primary(&mut self) -> PResult<Primary> {
|
||||||
use expression::Primary;
|
|
||||||
let token = self.peek()?;
|
let token = self.peek()?;
|
||||||
match token.ty() {
|
match token.ty() {
|
||||||
Type::Identifier => self.identifier().map(Primary::Identifier),
|
Type::Identifier => self.identifier().map(Primary::Identifier),
|
||||||
@ -465,7 +551,7 @@ impl Parser {
|
|||||||
/// Parses a [call expression](Call)
|
/// Parses a [call expression](Call)
|
||||||
fn call(&mut self) -> PResult<Call> {
|
fn call(&mut self) -> PResult<Call> {
|
||||||
let callee = self.primary()?;
|
let callee = self.primary()?;
|
||||||
let Ok(Type::LParen) = self.peek().map(Token::ty) else {
|
if self.matches(Type::LParen).is_err() {
|
||||||
return Ok(Call::Primary(callee));
|
return Ok(Call::Primary(callee));
|
||||||
};
|
};
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
@ -526,12 +612,11 @@ impl Parser {
|
|||||||
/// ```
|
/// ```
|
||||||
/// becomes
|
/// becomes
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// fn function_name(&mut self) -> PResult<ret::Value> { ... }
|
/// fn function_name(&mut self) -> PResult<ret::Value> { ... }
|
||||||
/// ```
|
/// ```
|
||||||
macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
|
macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
|
||||||
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")]
|
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](Operation::Binary) expression")]
|
||||||
fn $f (&mut self) -> PResult<math::Operation> {
|
fn $f (&mut self) -> PResult<Operation> {
|
||||||
use math::{Operation, Binary};
|
|
||||||
let (first, mut other) = (self.$a()?, vec![]);
|
let (first, mut other) = (self.$a()?, vec![]);
|
||||||
while let Ok(op) = self.$b() {
|
while let Ok(op) = self.$b() {
|
||||||
other.push((op, self.$a()?));
|
other.push((op, self.$a()?));
|
||||||
@ -543,9 +628,7 @@ macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
|
|||||||
)*}
|
)*}
|
||||||
/// # [Arithmetic and Logical Subexpressions](math)
|
/// # [Arithmetic and Logical Subexpressions](math)
|
||||||
impl Parser {
|
impl Parser {
|
||||||
fn assign(&mut self) -> PResult<math::Operation> {
|
fn assign(&mut self) -> PResult<Operation> {
|
||||||
use expression::Primary;
|
|
||||||
use math::{Assign, Operation};
|
|
||||||
let next = self.compare()?;
|
let next = self.compare()?;
|
||||||
let Ok(operator) = self.assign_op() else {
|
let Ok(operator) = self.assign_op() else {
|
||||||
return Ok(next);
|
return Ok(next);
|
||||||
@ -569,9 +652,8 @@ impl Parser {
|
|||||||
term = factor, term_op;
|
term = factor, term_op;
|
||||||
factor = unary, factor_op;
|
factor = unary, factor_op;
|
||||||
}
|
}
|
||||||
/// Parses a [unary operation](math::Operation::Unary) expression
|
/// Parses a [unary operation](Operation::Unary) expression
|
||||||
fn unary(&mut self) -> PResult<math::Operation> {
|
fn unary(&mut self) -> PResult<Operation> {
|
||||||
use math::{Operation, Unary};
|
|
||||||
let mut operators = vec![];
|
let mut operators = vec![];
|
||||||
while let Ok(op) = self.unary_op() {
|
while let Ok(op) = self.unary_op() {
|
||||||
operators.push(op)
|
operators.push(op)
|
||||||
@ -584,15 +666,15 @@ impl Parser {
|
|||||||
operand: self.primary_operation()?.into(),
|
operand: self.primary_operation()?.into(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/// Parses a [primary operation](math::Operation::Primary) expression
|
/// Parses a [primary operation](Operation::Primary) expression
|
||||||
fn primary_operation(&mut self) -> PResult<math::Operation> {
|
fn primary_operation(&mut self) -> PResult<Operation> {
|
||||||
Ok(math::Operation::Call(self.call()?))
|
Ok(Operation::Call(self.call()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) {
|
macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) {
|
||||||
$($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> {
|
$($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> {
|
||||||
use operator::Binary;
|
use operator::Binary;
|
||||||
let token = self.peek()?;
|
let token = self.peek().wrap(Error::not_operator())?;
|
||||||
let out = Ok(match token.ty() {
|
let out = Ok(match token.ty() {
|
||||||
$($type => Binary::$op,)*
|
$($type => Binary::$op,)*
|
||||||
_ => Err(Error::not_operator().token(token.clone()))?,
|
_ => Err(Error::not_operator().token(token.clone()))?,
|
||||||
@ -689,9 +771,8 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
/// # [Control Flow](control)
|
/// # [Control Flow](control)
|
||||||
impl Parser {
|
impl Parser {
|
||||||
/// Parses a [control flow](control::Flow) expression
|
/// Parses a [control flow](Flow) expression
|
||||||
fn flow(&mut self) -> PResult<control::Flow> {
|
fn flow(&mut self) -> PResult<Flow> {
|
||||||
use control::Flow;
|
|
||||||
use Keyword::{Break, Continue, For, If, Return, While};
|
use Keyword::{Break, Continue, For, If, Return, While};
|
||||||
let token = self.peek()?;
|
let token = self.peek()?;
|
||||||
match token.ty() {
|
match token.ty() {
|
||||||
@ -703,55 +784,47 @@ impl Parser {
|
|||||||
Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue),
|
Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue),
|
||||||
e => Err(Error::unexpected(e).token(token.clone()))?,
|
e => Err(Error::unexpected(e).token(token.clone()))?,
|
||||||
}
|
}
|
||||||
.map_err(|e| e.with_reason(IncompleteBranch))
|
.wrap(Error::not_branch())
|
||||||
}
|
}
|
||||||
/// Parses an [if](control::If) expression
|
/// Parses an [if](If) expression
|
||||||
fn parse_if(&mut self) -> PResult<control::If> {
|
fn parse_if(&mut self) -> PResult<If> {
|
||||||
self.keyword(Keyword::If)?;
|
self.keyword(Keyword::If)?;
|
||||||
Ok(control::If {
|
Ok(If { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? })
|
||||||
cond: self.expr()?.into(),
|
|
||||||
body: self.block()?,
|
|
||||||
else_: self.parse_else()?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Parses a [while](control::While) expression
|
/// Parses a [while](While) expression
|
||||||
fn parse_while(&mut self) -> PResult<control::While> {
|
fn parse_while(&mut self) -> PResult<While> {
|
||||||
self.keyword(Keyword::While)?;
|
self.keyword(Keyword::While)?;
|
||||||
Ok(control::While {
|
Ok(While { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? })
|
||||||
cond: self.expr()?.into(),
|
|
||||||
body: self.block()?,
|
|
||||||
else_: self.parse_else()?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Parses a [for](control::For) expression
|
/// Parses a [for](For) expression
|
||||||
fn parse_for(&mut self) -> PResult<control::For> {
|
fn parse_for(&mut self) -> PResult<For> {
|
||||||
self.keyword(Keyword::For)?;
|
self.keyword(Keyword::For)?;
|
||||||
Ok(control::For {
|
Ok(For {
|
||||||
var: self.identifier()?,
|
var: self.identifier()?,
|
||||||
iter: { self.keyword(Keyword::In)?.expr()?.into() },
|
iter: { self.keyword(Keyword::In)?.expr()?.into() },
|
||||||
body: self.block()?,
|
body: self.block()?,
|
||||||
else_: self.parse_else()?,
|
else_: self.parse_else()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Parses an [else](control::Else) sub-expression
|
/// Parses an [else](Else) sub-expression
|
||||||
fn parse_else(&mut self) -> PResult<Option<control::Else>> {
|
fn parse_else(&mut self) -> PResult<Option<Else>> {
|
||||||
// it's fine for `else` to be missing entirely
|
// it's fine for `else` to be missing entirely
|
||||||
self.keyword(Keyword::Else)
|
self.keyword(Keyword::Else)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|p| Ok(control::Else { expr: p.expr()?.into() }))
|
.map(|p| Ok(Else { expr: p.expr()?.into() }))
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
/// Parses a [break](control::Break) expression
|
/// Parses a [break](Break) expression
|
||||||
fn parse_break(&mut self) -> PResult<control::Break> {
|
fn parse_break(&mut self) -> PResult<Break> {
|
||||||
Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() })
|
Ok(Break { expr: self.keyword(Keyword::Break)?.expr()?.into() })
|
||||||
}
|
}
|
||||||
/// Parses a [return](control::Return) expression
|
/// Parses a [return](Return) expression
|
||||||
fn parse_return(&mut self) -> PResult<control::Return> {
|
fn parse_return(&mut self) -> PResult<Return> {
|
||||||
Ok(control::Return { expr: self.keyword(Keyword::Return)?.expr()?.into() })
|
Ok(Return { expr: self.keyword(Keyword::Return)?.expr()?.into() })
|
||||||
}
|
}
|
||||||
/// Parses a [continue](control::Continue) expression
|
/// Parses a [continue](Continue) expression
|
||||||
fn parse_continue(&mut self) -> PResult<control::Continue> {
|
fn parse_continue(&mut self) -> PResult<Continue> {
|
||||||
self.keyword(Keyword::Continue)?;
|
self.keyword(Keyword::Continue)?;
|
||||||
Ok(control::Continue)
|
Ok(Continue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast)
|
//! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast)
|
||||||
|
|
||||||
use super::ast::preamble::*;
|
use super::ast::preamble::*;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
@ -7,17 +8,14 @@ use std::{
|
|||||||
/// Prettily prints this node
|
/// Prettily prints this node
|
||||||
pub trait PrettyPrintable {
|
pub trait PrettyPrintable {
|
||||||
/// Prettily prints this node
|
/// Prettily prints this node
|
||||||
fn print(&self);
|
|
||||||
/// Prettily writes this node into the given [Writer](Write)
|
|
||||||
fn write(&self, into: impl Write) -> IOResult<()>;
|
|
||||||
}
|
|
||||||
impl PrettyPrintable for Start {
|
|
||||||
fn print(&self) {
|
fn print(&self) {
|
||||||
let _ = Printer::default().visit(self);
|
let _ = self.visit(&mut Printer::default());
|
||||||
}
|
}
|
||||||
|
/// Prettily writes this node into the given [Writer](Write)
|
||||||
fn write(&self, into: impl Write) -> IOResult<()> {
|
fn write(&self, into: impl Write) -> IOResult<()> {
|
||||||
Printer::from(into).visit(self)
|
self.visit(&mut Printer::from(into))
|
||||||
}
|
}
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a Conlang [syntax tree](crate::ast) into a [Writer](Write)
|
/// Prints a Conlang [syntax tree](crate::ast) into a [Writer](Write)
|
||||||
@ -26,6 +24,18 @@ pub struct Printer<W: Write> {
|
|||||||
level: u32,
|
level: u32,
|
||||||
writer: W,
|
writer: W,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<W: Write> Write for Printer<W> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> IOResult<usize> {
|
||||||
|
self.writer.write(buf)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> IOResult<()> {
|
||||||
|
self.writer.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'t> Default for Printer<StdoutLock<'t>> {
|
impl<'t> Default for Printer<StdoutLock<'t>> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { level: 0, writer: stdout().lock() }
|
Self { level: 0, writer: stdout().lock() }
|
||||||
@ -67,82 +77,154 @@ impl<W: Write> Printer<W> {
|
|||||||
macro visit_operator($self:ident.$op:expr) {
|
macro visit_operator($self:ident.$op:expr) {
|
||||||
$self.space()?.put($op)?.space().map(drop)
|
$self.space()?.put($op)?.space().map(drop)
|
||||||
}
|
}
|
||||||
impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|
||||||
fn visit_program(&mut self, prog: &Program) -> IOResult<()> {
|
impl PrettyPrintable for Start {
|
||||||
// delegate to the walker
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
for stmt in &prog.0 {
|
let Self(program) = self;
|
||||||
self.visit_statement(stmt)?;
|
program.visit(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Program {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Self(module) = self;
|
||||||
|
for decl in module {
|
||||||
|
decl.visit(p)?;
|
||||||
|
p.newline()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> {
|
}
|
||||||
match stmt {
|
impl PrettyPrintable for Stmt {
|
||||||
Stmt::Let(stmt) => self.visit_let(stmt)?,
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
Stmt::Fn(function) => self.visit_fn_decl(function)?,
|
match self {
|
||||||
Stmt::Expr(e) => {
|
Stmt::Let(value) => value.visit(p),
|
||||||
self.visit_expr(e)?;
|
Stmt::Fn(value) => value.visit(p),
|
||||||
self.put(';').map(drop)?
|
Stmt::Expr(value) => {
|
||||||
|
value.visit(p)?;
|
||||||
|
p.put(';')?.newline().map(drop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.newline().map(drop)
|
|
||||||
}
|
}
|
||||||
fn visit_let(&mut self, stmt: &Let) -> IOResult<()> {
|
}
|
||||||
let Let { name, mutable, ty, init } = stmt;
|
impl PrettyPrintable for Let {
|
||||||
self.put("let")?.space()?;
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Let { name: Name { name, mutable, ty }, init } = self;
|
||||||
|
p.put("let")?.space()?;
|
||||||
if *mutable {
|
if *mutable {
|
||||||
self.put("mut")?.space()?;
|
p.put("mut")?.space()?;
|
||||||
}
|
}
|
||||||
self.visit_identifier(name)?;
|
name.visit(p)?;
|
||||||
if let Some(ty) = ty {
|
if let Some(ty) = ty {
|
||||||
self.put(':')?.space()?.visit_identifier(ty)?;
|
ty.visit(p.put(':')?.space()?)?
|
||||||
}
|
}
|
||||||
if let Some(init) = init {
|
if let Some(init) = init {
|
||||||
self.space()?.put('=')?.space()?.visit_expr(init)?;
|
init.visit(p.space()?.put('=')?.space()?)?;
|
||||||
}
|
}
|
||||||
self.put(';').map(drop)
|
p.put(';').map(drop)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn visit_fn_decl(&mut self, function: &FnDecl) -> IOResult<()> {
|
impl PrettyPrintable for FnDecl {
|
||||||
let FnDecl { name, args, body } = function;
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
self.put("fn")?.space()?;
|
let FnDecl { name, args, body } = self;
|
||||||
self.visit_identifier(name)?;
|
p.put("fn")?.space()?;
|
||||||
self.space()?.put('(')?;
|
name.visit(p)?;
|
||||||
|
p.space()?.put('(')?;
|
||||||
for (idx, arg) in args.iter().enumerate() {
|
for (idx, arg) in args.iter().enumerate() {
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
self.put(',')?.space()?;
|
p.put(',')?.space()?;
|
||||||
}
|
}
|
||||||
self.visit_identifier(arg)?;
|
arg.visit(p)?;
|
||||||
}
|
}
|
||||||
self.put(')')?.space()?;
|
p.put(')')?.space()?;
|
||||||
self.visit_block(body)
|
body.visit(p)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Name {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
if self.mutable {
|
||||||
|
p.put("mut")?.space()?;
|
||||||
|
}
|
||||||
|
self.name.visit(p)?;
|
||||||
|
if let Some(ty) = &self.ty {
|
||||||
|
ty.visit(p.put(':')?.space()?)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for TypeExpr {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match self {
|
||||||
|
TypeExpr::TupleType(_tt) => todo!(),
|
||||||
|
TypeExpr::TypePath(t) => t.visit(p),
|
||||||
|
TypeExpr::Empty(_) => p.put("()").map(drop),
|
||||||
|
TypeExpr::Never(_) => p.put('!').map(drop),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Path {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Path { absolute, parts } = self;
|
||||||
|
if *absolute {
|
||||||
|
p.put("::")?;
|
||||||
|
}
|
||||||
|
for (idx, part) in parts.iter().enumerate() {
|
||||||
|
if idx != 0 { p.put("::")?;}
|
||||||
|
part.visit(p)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for PathPart {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match self {
|
||||||
|
PathPart::PathSuper => p.put("super").map(drop),
|
||||||
|
PathPart::PathSelf => p.put("self").map(drop),
|
||||||
|
PathPart::PathIdent(id) =>id.visit(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Block {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Block { let_count: _, statements, expr } = self;
|
||||||
|
p.put('{')?.indent().newline()?;
|
||||||
|
for stmt in statements {
|
||||||
|
stmt.visit(p)?;
|
||||||
|
}
|
||||||
|
for expr in expr {
|
||||||
|
expr.visit(p)?;
|
||||||
|
}
|
||||||
|
p.dedent().newline()?.put('}').map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Expr {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Expr(expr) = self;
|
||||||
|
expr.visit(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_assign(&mut self, assign: &math::Assign) -> IOResult<()> {
|
impl PrettyPrintable for Operation {
|
||||||
let math::Assign { target, operator, init } = assign;
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
self.visit_identifier(target)?;
|
match self {
|
||||||
self.visit_assign_op(operator)?;
|
Operation::Assign(value) => value.visit(p),
|
||||||
self.visit_operation(init)
|
Operation::Binary(value) => value.visit(p),
|
||||||
}
|
Operation::Unary(value) => value.visit(p),
|
||||||
fn visit_binary(&mut self, binary: &math::Binary) -> IOResult<()> {
|
Operation::Call(value) => value.visit(p),
|
||||||
let math::Binary { first, other } = binary;
|
|
||||||
self.put('(')?.visit_operation(first)?;
|
|
||||||
for (op, other) in other {
|
|
||||||
self.visit_binary_op(op)?;
|
|
||||||
self.visit_operation(other)?;
|
|
||||||
}
|
}
|
||||||
self.put(')').map(drop)
|
|
||||||
}
|
}
|
||||||
fn visit_unary(&mut self, unary: &math::Unary) -> IOResult<()> {
|
}
|
||||||
let math::Unary { operators, operand } = unary;
|
impl PrettyPrintable for Assign {
|
||||||
for op in operators {
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
self.visit_unary_op(op)?;
|
let Assign { target, operator, init } = self;
|
||||||
}
|
target.visit(p)?;
|
||||||
self.visit_operation(operand)
|
operator.visit(p)?;
|
||||||
|
init.visit(p)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> {
|
impl PrettyPrintable for operator::Assign {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
use operator::Assign;
|
use operator::Assign;
|
||||||
visit_operator!(self.match op {
|
visit_operator!(p.match self {
|
||||||
Assign::Assign => "=",
|
Assign::Assign => "=",
|
||||||
Assign::AddAssign => "+=",
|
Assign::AddAssign => "+=",
|
||||||
Assign::SubAssign => "-=",
|
Assign::SubAssign => "-=",
|
||||||
@ -156,9 +238,23 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|||||||
Assign::ShrAssign => ">>=",
|
Assign::ShrAssign => ">>=",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> IOResult<()> {
|
}
|
||||||
|
impl PrettyPrintable for Binary {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Binary { first, other } = self;
|
||||||
|
p.put('(')?;
|
||||||
|
first.visit(p)?;
|
||||||
|
for (op, other) in other {
|
||||||
|
op.visit(p)?;
|
||||||
|
other.visit(p)?
|
||||||
|
}
|
||||||
|
p.put(')').map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for operator::Binary {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
use operator::Binary;
|
use operator::Binary;
|
||||||
visit_operator!(self.match op {
|
visit_operator!(p.match self {
|
||||||
Binary::Mul => "*",
|
Binary::Mul => "*",
|
||||||
Binary::Div => "/",
|
Binary::Div => "/",
|
||||||
Binary::Rem => "%",
|
Binary::Rem => "%",
|
||||||
@ -182,9 +278,20 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|||||||
Binary::Greater => ">",
|
Binary::Greater => ">",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> {
|
}
|
||||||
|
impl PrettyPrintable for Unary {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Unary { operators, operand } = self;
|
||||||
|
for op in operators {
|
||||||
|
op.visit(p)?;
|
||||||
|
}
|
||||||
|
operand.visit(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for operator::Unary {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
use operator::Unary;
|
use operator::Unary;
|
||||||
self.put(match op {
|
p.put(match self {
|
||||||
Unary::RefRef => "&&",
|
Unary::RefRef => "&&",
|
||||||
Unary::Deref => "*",
|
Unary::Deref => "*",
|
||||||
Unary::Ref => "&",
|
Unary::Ref => "&",
|
||||||
@ -196,108 +303,155 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|||||||
})
|
})
|
||||||
.map(drop)
|
.map(drop)
|
||||||
}
|
}
|
||||||
fn visit_if(&mut self, expr: &control::If) -> IOResult<()> {
|
}
|
||||||
self.put("while")?.space()?.visit_expr(&expr.cond)?;
|
|
||||||
self.space()?.visit_block(&expr.body)?;
|
|
||||||
match &expr.else_ {
|
|
||||||
Some(e) => self.visit_else(e),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_while(&mut self, expr: &control::While) -> IOResult<()> {
|
|
||||||
self.put("while")?.space()?.visit_expr(&expr.cond)?;
|
|
||||||
self.space()?.visit_block(&expr.body)?;
|
|
||||||
match &expr.else_ {
|
|
||||||
Some(e) => self.visit_else(e),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_for(&mut self, expr: &control::For) -> IOResult<()> {
|
|
||||||
self.put("for")?.space()?.visit_identifier(&expr.var)?;
|
|
||||||
self.space()?.put("in")?.space()?.visit_expr(&expr.iter)?;
|
|
||||||
self.space()?.visit_block(&expr.body)?;
|
|
||||||
match &expr.else_ {
|
|
||||||
Some(e) => self.visit_else(e),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_else(&mut self, else_: &control::Else) -> IOResult<()> {
|
|
||||||
self.space()?.put("else")?.space()?.visit_expr(&else_.expr)
|
|
||||||
}
|
|
||||||
fn visit_continue(&mut self, _: &control::Continue) -> IOResult<()> {
|
|
||||||
self.put("continue").map(drop)
|
|
||||||
}
|
|
||||||
fn visit_break(&mut self, brk: &control::Break) -> IOResult<()> {
|
|
||||||
self.put("break")?.space()?.visit_expr(&brk.expr)
|
|
||||||
}
|
|
||||||
fn visit_return(&mut self, ret: &control::Return) -> IOResult<()> {
|
|
||||||
self.put("return")?.space()?.visit_expr(&ret.expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_identifier(&mut self, ident: &Identifier) -> IOResult<()> {
|
impl PrettyPrintable for Call {
|
||||||
self.put(&ident.0).map(drop)
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
}
|
match self {
|
||||||
fn visit_string_literal(&mut self, string: &str) -> IOResult<()> {
|
Call::FnCall(value) => value.visit(p),
|
||||||
self.put("\"")?.put(string)?.put("\"").map(drop)
|
Call::Primary(value) => value.visit(p),
|
||||||
}
|
|
||||||
fn visit_char_literal(&mut self, char: &char) -> IOResult<()> {
|
|
||||||
self.put("'")?.put(char)?.put("'").map(drop)
|
|
||||||
}
|
|
||||||
fn visit_bool_literal(&mut self, bool: &bool) -> IOResult<()> {
|
|
||||||
self.put(bool).map(drop)
|
|
||||||
}
|
|
||||||
fn visit_float_literal(&mut self, float: &literal::Float) -> IOResult<()> {
|
|
||||||
self.put(float.sign)?
|
|
||||||
.put(float.exponent)?
|
|
||||||
.put(float.mantissa)
|
|
||||||
.map(drop)
|
|
||||||
}
|
|
||||||
fn visit_int_literal(&mut self, int: &u128) -> IOResult<()> {
|
|
||||||
self.put(int).map(drop)
|
|
||||||
}
|
|
||||||
fn visit_empty(&mut self) -> IOResult<()> {
|
|
||||||
self.put("()").map(drop)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_block(&mut self, block: &expression::Block) -> IOResult<()> {
|
|
||||||
self.put('{')?.indent().newline()?;
|
|
||||||
for stmt in &block.statements {
|
|
||||||
self.visit_statement(stmt)?;
|
|
||||||
}
|
}
|
||||||
for expr in &block.expr {
|
|
||||||
self.visit_expr(expr)?;
|
|
||||||
}
|
|
||||||
self.dedent().newline()?.put('}').map(drop)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn visit_tuple(&mut self, tuple: &Tuple) -> IOResult<()> {
|
impl PrettyPrintable for FnCall {
|
||||||
for (idx, expr) in tuple.elements.iter().enumerate() {
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
if idx > 0 {
|
let FnCall { callee, args } = self;
|
||||||
self.put(',')?.space()?;
|
callee.visit(p)?;
|
||||||
}
|
|
||||||
self.visit_expr(expr)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_group(&mut self, expr: &Group) -> IOResult<()> {
|
|
||||||
self.put('(')?;
|
|
||||||
match expr {
|
|
||||||
Group::Tuple(tuple) => self.space()?.visit_tuple(tuple),
|
|
||||||
Group::Single(expr) => self.space()?.visit_expr(expr),
|
|
||||||
Group::Empty => self.visit_empty(),
|
|
||||||
}?;
|
|
||||||
self.space()?.put(')').map(drop)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_fn_call(&mut self, call: &FnCall) -> IOResult<()> {
|
|
||||||
let FnCall { callee, args } = call;
|
|
||||||
self.visit_primary(callee)?;
|
|
||||||
for arg_list in args {
|
for arg_list in args {
|
||||||
self.put('(')?;
|
p.put('(')?;
|
||||||
self.visit_tuple(arg_list)?;
|
arg_list.visit(p)?;
|
||||||
self.put(')')?;
|
p.put(')')?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl PrettyPrintable for Primary {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match self {
|
||||||
|
Primary::Identifier(value) => value.visit(p),
|
||||||
|
Primary::Literal(value) => value.visit(p),
|
||||||
|
Primary::Block(value) => value.visit(p),
|
||||||
|
Primary::Group(value) => value.visit(p),
|
||||||
|
Primary::Branch(value) => value.visit(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Group {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
p.put('(')?;
|
||||||
|
match self {
|
||||||
|
Group::Tuple(tuple) => tuple.visit(p.space()?)?,
|
||||||
|
Group::Single(expr) => expr.visit(p.space()?)?,
|
||||||
|
Group::Empty => (),
|
||||||
|
};
|
||||||
|
p.space()?.put(')').map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Tuple {
|
||||||
|
/// Writes a *non-delimited* [Tuple] to the [Printer]
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Tuple { elements } = self;
|
||||||
|
for (idx, expr) in elements.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
p.put(',')?.space()?;
|
||||||
|
}
|
||||||
|
expr.visit(p)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyPrintable for Identifier {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Identifier { name, index: _ } = self; // TODO: Pretty-print variable number as well
|
||||||
|
p.put(name).map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Literal {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match self {
|
||||||
|
Literal::String(value) => write!(p, "\"{value}\""),
|
||||||
|
Literal::Char(value) => write!(p, "'{value}'"),
|
||||||
|
Literal::Bool(value) => write!(p, "{value}"),
|
||||||
|
Literal::Float(value) => value.visit(p),
|
||||||
|
Literal::Int(value) => write!(p, "{value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Float {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Float { sign, exponent, mantissa } = self;
|
||||||
|
p.put(sign)?.put(exponent)?.put(mantissa).map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyPrintable for Flow {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
match self {
|
||||||
|
Flow::While(value) => value.visit(p),
|
||||||
|
Flow::If(value) => value.visit(p),
|
||||||
|
Flow::For(value) => value.visit(p),
|
||||||
|
Flow::Continue(value) => value.visit(p),
|
||||||
|
Flow::Return(value) => value.visit(p),
|
||||||
|
Flow::Break(value) => value.visit(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for While {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let While { cond, body, else_ } = self;
|
||||||
|
cond.visit(p.put("while")?.space()?)?;
|
||||||
|
body.visit(p.space()?)?;
|
||||||
|
match else_ {
|
||||||
|
Some(e) => e.visit(p),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for If {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let If { cond, body, else_ } = self;
|
||||||
|
cond.visit(p.put("if")?.space()?)?;
|
||||||
|
body.visit(p.space()?)?;
|
||||||
|
match else_ {
|
||||||
|
Some(e) => e.visit(p),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for For {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let For { var, iter, body, else_ } = self;
|
||||||
|
var.visit(p.put("for")?.space()?)?;
|
||||||
|
iter.visit(p.space()?.put("in")?.space()?)?;
|
||||||
|
body.visit(p.space()?)?;
|
||||||
|
match else_ {
|
||||||
|
Some(e) => e.visit(p),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Else {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Else { expr } = self;
|
||||||
|
expr.visit(p.space()?.put("else")?.space()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Continue {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let control::Continue = self; // using pattern destructuring, rather than assigning to "Continue"
|
||||||
|
p.put("continue").map(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Break {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Break { expr } = self;
|
||||||
|
expr.visit(p.put("break")?.space()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrintable for Return {
|
||||||
|
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
|
||||||
|
let Return { expr } = self;
|
||||||
|
expr.visit(p.put("return")?.space()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -87,6 +87,9 @@ pub enum Keyword {
|
|||||||
Let,
|
Let,
|
||||||
Mut,
|
Mut,
|
||||||
Return,
|
Return,
|
||||||
|
SelfKw,
|
||||||
|
SelfTy,
|
||||||
|
Super,
|
||||||
True,
|
True,
|
||||||
While,
|
While,
|
||||||
}
|
}
|
||||||
@ -174,6 +177,9 @@ impl Display for Keyword {
|
|||||||
Self::Let => "let".fmt(f),
|
Self::Let => "let".fmt(f),
|
||||||
Self::Mut => "mut".fmt(f),
|
Self::Mut => "mut".fmt(f),
|
||||||
Self::Return => "return".fmt(f),
|
Self::Return => "return".fmt(f),
|
||||||
|
Self::SelfKw => "self".fmt(f),
|
||||||
|
Self::SelfTy => "Self".fmt(f),
|
||||||
|
Self::Super => "super".fmt(f),
|
||||||
Self::True => "true".fmt(f),
|
Self::True => "true".fmt(f),
|
||||||
Self::While => "while".fmt(f),
|
Self::While => "while".fmt(f),
|
||||||
}
|
}
|
||||||
@ -195,6 +201,9 @@ impl FromStr for Keyword {
|
|||||||
"let" => Self::Let,
|
"let" => Self::Let,
|
||||||
"mut" => Self::Mut,
|
"mut" => Self::Mut,
|
||||||
"return" => Self::Return,
|
"return" => Self::Return,
|
||||||
|
"self" => Self::SelfKw,
|
||||||
|
"Self" => Self::SelfTy,
|
||||||
|
"super" => Self::Super,
|
||||||
"true" => Self::True,
|
"true" => Self::True,
|
||||||
"while" => Self::While,
|
"while" => Self::While,
|
||||||
_ => Err(())?,
|
_ => Err(())?,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// Calculate Fibonacci numbers
|
// Calculate Fibonacci numbers
|
||||||
|
|
||||||
fn main() -> i128 {
|
fn main() -> i128 {
|
||||||
print("fib(10):");
|
print("fib(10): ", fib(10));
|
||||||
fib(10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements the classic recursive definition of fib()
|
/// Implements the classic recursive definition of fib()
|
||||||
|
Loading…
Reference in New Issue
Block a user