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:
John 2024-01-04 02:18:09 -06:00
parent 9b7cf9c017
commit 79fda16788
14 changed files with 1284 additions and 563 deletions

View File

@ -1,10 +1,10 @@
[workspace]
members = ["libconlang"]
members = ["libconlang", "cl-frontend"]
resolver = "2"
[workspace.package]
repository = "https://git.soft.fish/j/Conlang"
version = "0.0.1"
version = "0.0.2"
authors = ["John Breaux <j@soft.fish>"]
edition = "2021"
license = "MIT"

13
cl-frontend/Cargo.toml Normal file
View 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
View 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
View 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()
}

View File

@ -1,7 +1,14 @@
(* Conlang Expression Grammar *)
Start = Program ;
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 = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
Bool = "true" | "false" ;
@ -10,11 +17,22 @@ Identifier = IDENTIFIER ;
(* # Statements *)
(* statement *)
Stmt = Fn | Let | Expr ';' ;
Let = "let" "mut"? Identifier (':' Type)? ('=' Expr)? ';' ;
Let = "let" Name ('=' Expr)? ';' ;
Fn = "fn" Identifier '(' Params? ')' Block ;
(* TODO: Type system *)
Params = (Param ',')* Param? ;
Param = Identifier (*':' Type *) ;
Params = (Name ',')* Name? ;
Name = "mut"? Identifier (':' Type )? ;
(* # Type Expressions *)
(* types *)
TypeExpr = Never | Empty | TypeTuple | PathExpr ;
Never = '!' ;
Empty = '(' ')' ;
TypeTuple = '(' (TypeExpr ',')* TypeExpr? ')' ;
PathExpr = '::'? PathPart ;
PathPart = "super" | Identifier ;
(* # Expressions *)
(* expression *)

View File

@ -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!();
}

View File

@ -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(())
}
}
}
}

View File

@ -11,19 +11,16 @@
//! See [statement], [literal], and [expression] for more information.
pub mod preamble {
#![allow(deprecated)]
//! Common imports for working with the [ast](super)
pub use super::{
expression::{
self,
call::*,
control,
math::{self, operator},
tuple::*,
},
literal,
expression::{call::*, control::*, math::*, tuple::*, *},
literal::*,
path::*,
statement::*,
types::*,
visitor::Visitor,
Identifier, Program, Start,
*,
};
}
@ -43,47 +40,9 @@ pub struct Program(pub Vec<statement::Stmt>);
/// # Syntax
/// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Identifier(pub String);
pub mod todo {
//! 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 struct Identifier {
pub name: String,
pub index: Option<usize>,
}
pub mod literal {
@ -150,6 +109,7 @@ pub mod statement {
use super::{
expression::{Block, Expr},
types::TypeExpr,
Identifier,
};
@ -164,7 +124,7 @@ pub mod statement {
Let(Let),
/// Contains a function declaration
/// # Syntax
/// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'` [`Block`]
/// [`Fn`](Stmt::Fn) := `"fn"` [`Identifier`] `'('` `Args...` `')'` [`Block`]
Fn(FnDecl),
/// Contains an expression statement
/// # Syntax
@ -177,26 +137,104 @@ pub mod statement {
/// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;`
#[derive(Clone, Debug)]
pub struct Let {
pub name: Identifier,
pub mutable: bool,
pub ty: Option<Identifier>,
pub name: Name,
pub init: Option<Expr>,
}
/// Contains a function declaration
/// # Syntax
/// [`FnDecl`] := `"fn"` [`Identifier`] `'('` [`Tuple`] `')'`
/// [`FnDecl`] := `"fn"` [`Identifier`] `'('` `Args...` `')'`
#[derive(Clone, Debug)]
pub struct FnDecl {
pub name: Identifier,
pub args: Vec<Identifier>,
pub name: Name,
pub args: Vec<Name>,
pub body: Block,
// 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
}
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 {
//! # Expressions
//!
@ -219,17 +257,19 @@ pub mod expression {
//! | 9 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`)
//! | 9 | [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */
//! | 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
//! [`Expr`]` := `[`math::Operation`] \
//! [`Block`]` := '{' `[`Expr`]` '}'` \
//! [`Group`]` := '(' `[`Expr`]`? ')'` \
//! [`Primary`]` := `[`Identifier`]` | `[`Literal`](literal::Literal)` | `[`Block`]` |
//! `[`Group`]` | `[`control::Flow`]
//! [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` |
//! `[`Group`]` | `[`Flow`]
//!
//! 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
///
@ -241,18 +281,18 @@ pub mod expression {
/// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest
/// derivation)
/// # Syntax
/// [`Primary`]` := `[`IDENTIFIER`](Identifier)`
/// | `[`Literal`](literal::Literal)`
/// | `[`Block`]`
/// | `[`Group`](tuple::Group)`
/// | `[`Branch`](control::Flow)
/// [`Primary`]` := `[`Identifier`]`
/// | `[`Literal`]`
/// | `[`Block`]`
/// | `[`Group`]`
/// | `[`Branch`](Flow)
#[derive(Clone, Debug)]
pub enum Primary {
Identifier(Identifier),
Literal(literal::Literal),
Literal(Literal),
Block(Block),
Group(tuple::Group),
Branch(control::Flow),
Group(Group),
Branch(Flow),
}
/// Contains a Block Expression
@ -260,6 +300,7 @@ pub mod expression {
/// [`Block`] := `'{'` [`Expr`] `'}'`
#[derive(Clone, Debug)]
pub struct Block {
pub let_count: Option<usize>,
pub statements: Vec<Stmt>,
pub expr: Option<Box<Expr>>,
}
@ -352,7 +393,7 @@ pub mod expression {
//! [`Shift`][2]` := `[`Term`][2]` (`[`ShiftOp`][5]` `[`Term`][2]` )*` \
//! [`Term`][2]` := `[`Factor`][2]` (`[`TermOp`][5]` `[`Factor`][2]` )*` \
//! [`Factor`][2]` := `[`Unary`][1]` (`[`FactorOp`][5]` `[`Unary`][1]` )*` \
//! [`Unary`][1]` := (`[`UnaryOp`][4]`)* `[`FnCall`][7]
//! [`Unary`][1]` := (`[`UnaryOp`][4]`)* `[`Call`]
//!
//! [1]: Operation::Unary
//! [2]: Operation::Binary
@ -716,6 +757,7 @@ pub mod visitor {
};
/// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super)
#[deprecated]
pub trait Visitor<R> {
/// Visit the start of an AST
fn visit(&mut self, start: &Start) -> R {
@ -733,9 +775,9 @@ pub mod visitor {
}
}
/// 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)
fn visit_fn_decl(&mut self, function: &FnDecl) -> R;
fn visit_fn_decl(&mut self, decl: &FnDecl) -> R;
/// Visit an [Expression](Expr)
fn visit_expr(&mut self, expr: &Expr) -> R {
@ -781,10 +823,11 @@ pub mod visitor {
/// Visit a [Unary] Operation
fn visit_unary(&mut self, unary: &Unary) -> R;
// Math operators
/// Visit an [Assignment](Assign) [operator](operator::Assign)
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;
/// Visit a [Unary](Operation::Unary) [operator](operator::Unary)
/// Visit a [Unary] [operator](operator::Unary)
fn visit_unary_op(&mut self, op: &operator::Unary) -> R;
/// Visit a [Primary] expression
@ -796,7 +839,7 @@ pub mod visitor {
Primary::Literal(v) => self.visit_literal(v),
Primary::Block(v) => self.visit_block(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`]`
/// | `[`Continue`]` | `[`Return`]` | `[`Break`]
fn visit_branch_expr(&mut self, expr: &Flow) -> R {
match expr {
fn visit_branch(&mut self, flow: &Flow) -> R {
match flow {
Flow::While(e) => self.visit_while(e),
Flow::If(e) => self.visit_if(e),
Flow::For(e) => self.visit_for(e),
@ -859,3 +902,46 @@ pub mod visitor {
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
}
}

View File

@ -1,4 +1,5 @@
//! Interprets an AST as a program
#![allow(deprecated)] // TODO: REMOVE
use crate::ast::preamble::*;
use error::{Error, IResult};
@ -9,7 +10,7 @@ use temp_type_impl::ConValue;
pub trait Callable: std::fmt::Debug {
/// Calls this [Callable] in the provided [Interpreter], with [ConValue] args \
/// 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.
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 {
Self::Function(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<()> {
let Let { name: Identifier(name), init, .. } = stmt;
let Let { name: Name { name: Identifier { name, .. }, .. }, init, .. } = stmt;
if let Some(init) = init {
self.visit_expr(init)?;
let init = self.pop()?;
@ -394,7 +395,8 @@ impl Visitor<IResult<()>> for Interpreter {
let (ConValue::Tuple(args), callee) = self.pop_two()? else {
Err(Error::TypeError)?
};
callee.call(self, &args)?;
let return_value = callee.call(self, &args)?;
self.push(return_value);
}
Ok(())
}
@ -404,7 +406,8 @@ impl Visitor<IResult<()>> for Interpreter {
let math::Assign { target, operator, init } = assign;
self.visit_operation(init)?;
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 {
use std::mem::discriminant as variant;
// runtime typecheck
@ -418,9 +421,11 @@ impl Visitor<IResult<()>> for Interpreter {
self.push(ConValue::Empty);
return Ok(());
}
let Some(target) = resolved.as_mut() else {
Err(Error::NotInitialized(target.0.to_owned()))?
Err(Error::NotInitialized(target.name.to_owned()))?
};
match operator {
Assign::AddAssign => target.add_assign(init)?,
Assign::SubAssign => target.sub_assign(init)?,
@ -434,12 +439,13 @@ impl Visitor<IResult<()>> for Interpreter {
Assign::ShrAssign => target.shr_assign(init)?,
_ => (),
}
self.push(ConValue::Empty);
Ok(())
}
fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> {
use math::Binary;
let Binary { first, other } = bin;
self.visit_operation(first)?;
@ -469,15 +475,19 @@ impl Visitor<IResult<()>> for Interpreter {
}
}
}
Ok(())
}
fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> {
let math::Unary { operand, operators } = unary;
let Unary { operand, operators } = unary;
self.visit_operation(operand)?;
for op in operators.iter().rev() {
self.visit_unary_op(op)?;
}
Ok(())
}
@ -488,6 +498,7 @@ impl Visitor<IResult<()>> for Interpreter {
fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> {
use operator::Binary;
let (second, first) = self.pop_two()?;
let out = match op {
Binary::Mul => first * second,
Binary::Div => first / second,
@ -511,12 +522,14 @@ impl Visitor<IResult<()>> for Interpreter {
Binary::GreaterEq => first.gt_eq(&second),
Binary::Greater => first.gt(&second),
}?;
self.push(out);
Ok(())
}
fn visit_unary_op(&mut self, op: &operator::Unary) -> IResult<()> {
let operand = self.pop()?;
self.push(match op {
operator::Unary::RefRef => todo!(),
operator::Unary::Ref => todo!(),
@ -530,10 +543,11 @@ impl Visitor<IResult<()>> for Interpreter {
}
operator::Unary::Tilde => todo!(),
});
Ok(())
}
fn visit_if(&mut self, expr: &control::If) -> IResult<()> {
fn visit_if(&mut self, expr: &If) -> IResult<()> {
self.visit_expr(&expr.cond)?;
if self.pop()?.truthy()? {
self.visit_block(&expr.body)?;
@ -545,7 +559,7 @@ impl Visitor<IResult<()>> for Interpreter {
Ok(())
}
fn visit_while(&mut self, expr: &control::While) -> IResult<()> {
fn visit_while(&mut self, expr: &While) -> IResult<()> {
while {
self.visit_expr(&expr.cond)?;
self.pop()?.truthy()?
@ -581,7 +595,7 @@ impl Visitor<IResult<()>> for Interpreter {
_ => Err(Error::NotIterable)?,
};
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 {
self.pop()?;
continue;
@ -627,7 +641,7 @@ impl Visitor<IResult<()>> for Interpreter {
}
fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> {
let value = self.resolve(&ident.0)?;
let value = self.resolve(&ident.name)?;
self.push(value);
Ok(())
}
@ -670,8 +684,17 @@ impl Default for Interpreter {
pub mod function {
//! Represents a block of code which lives inside the Interpreter
use super::{Callable, ConValue, Error, FnDecl, IResult, Identifier, Interpreter};
use crate::ast::visitor::Visitor;
use super::{
// 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
#[derive(Clone, Debug)]
pub struct Function {
@ -679,19 +702,23 @@ pub mod function {
declaration: Box<FnDecl>,
// /// Stores the enclosing scope of the function
// TODO: Capture variables
//environment: Box<Environment>,
}
impl Function {
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 {
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
if args.len() != self.declaration.args.len() {
return Err(Error::ArgNumber {
@ -701,17 +728,19 @@ pub mod function {
}
// TODO: Isolate cross-function scopes!
interpreter.scope.enter();
for (Identifier(arg), value) in self.declaration.args.iter().zip(args) {
interpreter.scope.insert(arg, Some(value.clone()));
for (Name { name: Identifier { name, .. }, .. }, value) in
self.declaration.args.iter().zip(args)
{
interpreter.scope.insert(name, Some(value.clone()));
}
match interpreter.visit_block(&self.declaration.body) {
Err(Error::Return(value)) => interpreter.push(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value))?,
Err(e) => Err(e)?,
Ok(_) => (),
Ok(()) => (),
}
interpreter.scope.exit()?;
Ok(())
interpreter.pop()
}
}
}
@ -719,14 +748,12 @@ pub mod function {
pub mod builtin {
mod builtin_imports {
pub use crate::interpreter::{
error::{Error, IResult},
temp_type_impl::ConValue,
BuiltIn, Callable, Interpreter,
error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, Interpreter,
};
}
use super::BuiltIn;
/// 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 {
//! Implements the unstable `print(...)` builtin
@ -738,11 +765,12 @@ pub mod builtin {
#[rustfmt::skip]
impl Callable for Print {
fn name(&self) -> &'static str { "print" }
fn call(&self, inter: &mut Interpreter, args: &[ConValue]) -> IResult<()> {
for arg in args { print!("{arg}") }
fn call(&self, _inter: &mut Interpreter, args: &[ConValue]) -> IResult<ConValue> {
for arg in args {
print!("{arg}")
}
println!();
inter.push(ConValue::Empty);
Ok(())
Ok(ConValue::Empty)
}
}
}
@ -755,10 +783,27 @@ pub mod builtin {
#[rustfmt::skip]
impl Callable for 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:?}");
inter.push(args);
Ok(())
Ok(args.into())
}
}
}
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,
FnDecl,
};
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Display};
/// Implements a nested lexical scope
#[derive(Clone, Debug, Default)]
@ -783,6 +828,19 @@ pub mod scope {
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 {
pub fn new() -> Self {
let mut out = Self::default();
@ -835,8 +893,10 @@ pub mod scope {
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
self.vars
.insert(decl.name.0.clone(), Some(Function::new(decl).into()));
self.vars.insert(
decl.name.name.name.clone(),
Some(Function::new(decl).into()),
);
}
}
}

View File

@ -1,18 +1,37 @@
//! Parses [tokens](super::token) into an [AST](super::ast)
use super::{ast::preamble::*, lexer::Lexer, token::preamble::*};
use error::{Error, Reason::*, *};
use error::{Error, *};
pub mod error {
use super::{Token, Type};
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]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Reason {
Expected(Type),
Unexpected(Type),
NotPathSegment(Type),
NotIdentifier,
NotStatement,
NotLet,
NotFnDecl,
NotOperator,
NotLiteral,
NotString,
@ -37,7 +56,11 @@ pub mod error {
match self {
Self::Expected(t) => write!(f, "Expected {t}"),
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::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::NotLiteral => "Not a literal".fmt(f),
Self::NotString => "Not a string".fmt(f),
@ -67,11 +90,15 @@ pub mod error {
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Error {
reason: Reason,
child: Option<Box<Self>>,
start: Option<Token>,
}
impl std::error::Error for Error {}
impl Display for Error {
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 {
write!(f, "{}:{}: ", token.line(), token.col())?;
}
@ -84,7 +111,7 @@ pub mod error {
#[doc = concat!("[`", stringify!($reason), "`]")]
#[allow(dead_code)]
pub(crate) fn $fn($($($p : $t),*)?) -> Self {
Self { reason: $reason$(($($p)*))?, start: None }
Self { reason: $reason$(($($p)*))?, child: None, start: None }
}
)*}
impl Error {
@ -104,14 +131,14 @@ pub mod error {
pub fn reason(&self) -> Reason {
self.reason
}
/// Modifies the [Reason] of this error
pub fn with_reason(self, reason: Reason) -> Self {
Self { reason, ..self }
}
error_impl! {
expected(e: Type): Expected,
unexpected(e: Type): Unexpected,
not_path_segment(e: Type): NotPathSegment,
not_identifier: NotIdentifier,
not_statement: NotStatement,
not_let: NotLet,
not_fn_decl: NotFnDecl,
not_operator: NotOperator,
not_literal: NotLiteral,
not_string: NotString,
@ -160,20 +187,25 @@ impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
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)
pub fn parse(&mut self) -> PResult<Start> {
self.consume_comments();
Ok(Start(self.program()?))
}
/// Parses only one expression
pub fn parse_expr(&mut self) -> PResult<expression::Expr> {
pub fn parse_expr(&mut self) -> PResult<Expr> {
self.expr()
}
/// Peeks at the current token
pub fn peek(&self) -> PResult<&Token> {
self.tokens
.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
fn consume_comments(&mut self) -> &mut Self {
@ -212,9 +244,7 @@ impl Parser {
/// Advances forward until a token with type [`t`](Type) is encountered
fn advance_until(&mut self, t: Type) -> PResult<&mut Self> {
while self.matches(t).is_err() {
self.check_eof()
.map_err(|e| e.with_reason(Expected(t)))?
.consume();
self.check_eof().wrap(Error::expected(t))?.consume();
}
Ok(self)
}
@ -244,10 +274,11 @@ impl Parser {
fn matches(&mut self, t: Type) -> PResult<&Token> {
let token = self.check_eof()?.peek().expect("self should not be eof");
if token.ty() != t {
Err(Error::expected(t).token(token.clone()))?
}
Err(Error::expected(t).token(token.clone()))
} else {
Ok(token)
}
}
/// Consumes, without returning, a token with the given [Keyword], or returns an error.
///
/// Useful if you only want to check the existence of a [Keyword]
@ -282,7 +313,7 @@ impl Parser {
/// Parses an [Identifier]
fn identifier(&mut self) -> PResult<Identifier> {
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())?,
};
self.consume();
@ -363,30 +394,25 @@ impl Parser {
fn stmt(&mut self) -> PResult<Stmt> {
let token = self.peek()?;
match token.ty() {
Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let),
Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn),
Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let).wrap(Error::not_let()),
Type::Keyword(Keyword::Fn) => self.fn_decl().map(Stmt::Fn).wrap(Error::not_fn_decl()),
_ => {
let out = Stmt::Expr(self.expr()?);
self.consume_type(Type::Semi)?;
Ok(out)
}
}
.wrap(Error::not_statement())
}
/// Parses a [Let] statement
fn let_stmt(&mut self) -> PResult<Let> {
let out = Let {
mutable: self.consume().keyword(Keyword::Mut).is_ok(),
name: self.identifier()?,
ty: self
.consume_type(Type::Colon)
.and_then(Self::identifier)
.ok(),
init: self.consume_type(Type::Eq).and_then(Self::expr).ok(),
};
self.keyword(Keyword::Let)?;
let out =
Let { name: self.name()?, init: self.consume_type(Type::Eq).and_then(Self::expr).ok() };
self.consume_type(Type::Semi)?;
Ok(out)
}
/// Parses a [Function] statement
/// Parses a [function declaration](FnDecl) statement
fn fn_decl(&mut self) -> PResult<FnDecl> {
self.keyword(Keyword::Fn)?;
let name = self.identifier()?;
@ -394,37 +420,98 @@ impl Parser {
let args = self.params()?;
self.consume_type(Type::RParen)?;
// TODO: Parse type-expressions and store return types in the AST
if self.consume_type(Type::Arrow).is_ok() {
self.expr()?;
let ty = if self.consume_type(Type::Arrow).is_ok() {
Some(self.type_expr()?)
} else {
None
};
Ok(FnDecl { name: Name { name, mutable: false, ty }, args, body: self.block()? })
}
Ok(FnDecl { name, args, body: self.block()? })
}
fn params(&mut self) -> PResult<Vec<Identifier>> {
/// Parses a [parameter](Name) list for [FnDecl]
fn params(&mut self) -> PResult<Vec<Name>> {
let mut args = vec![];
while let Ok(ident) = self.identifier() {
args.push(ident);
if self.consume_type(Type::Colon).is_ok() {
// TODO: Parse type-expressions and make this mandatory
self.expr()?;
}
while let Ok(name) = self.name() {
args.push(name);
if self.consume_type(Type::Comma).is_err() {
break;
}
}
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
impl Parser {
/// Parses an [expression](expression::Expr)
fn expr(&mut self) -> PResult<expression::Expr> {
use expression::Expr;
/// Parses an [expression](Expr)
fn expr(&mut self) -> PResult<Expr> {
Ok(Expr(self.assign()?))
}
/// Parses a [block expression](expression::Block)
fn block(&mut self) -> PResult<expression::Block> {
use expression::{Block, Expr};
/// Parses a [block expression](Block)
fn block(&mut self) -> PResult<Block> {
let mut statements = vec![];
let mut expr: Option<Box<Expr>> = None;
self.consume_type(Type::LCurly)?;
@ -440,11 +527,10 @@ impl Parser {
Err(_) => statements.push(self.stmt()?),
}
}
Ok(Block { statements, expr })
Ok(Block { statements, expr, let_count: None })
}
/// Parses a [primary expression](expression::Primary)
fn primary(&mut self) -> PResult<expression::Primary> {
use expression::Primary;
/// Parses a [primary expression](Primary)
fn primary(&mut self) -> PResult<Primary> {
let token = self.peek()?;
match token.ty() {
Type::Identifier => self.identifier().map(Primary::Identifier),
@ -465,7 +551,7 @@ impl Parser {
/// Parses a [call expression](Call)
fn call(&mut self) -> PResult<Call> {
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));
};
let mut args = vec![];
@ -529,9 +615,8 @@ impl Parser {
/// fn function_name(&mut self) -> PResult<ret::Value> { ... }
/// ```
macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")]
fn $f (&mut self) -> PResult<math::Operation> {
use math::{Operation, Binary};
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](Operation::Binary) expression")]
fn $f (&mut self) -> PResult<Operation> {
let (first, mut other) = (self.$a()?, vec![]);
while let Ok(op) = self.$b() {
other.push((op, self.$a()?));
@ -543,9 +628,7 @@ macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
)*}
/// # [Arithmetic and Logical Subexpressions](math)
impl Parser {
fn assign(&mut self) -> PResult<math::Operation> {
use expression::Primary;
use math::{Assign, Operation};
fn assign(&mut self) -> PResult<Operation> {
let next = self.compare()?;
let Ok(operator) = self.assign_op() else {
return Ok(next);
@ -569,9 +652,8 @@ impl Parser {
term = factor, term_op;
factor = unary, factor_op;
}
/// Parses a [unary operation](math::Operation::Unary) expression
fn unary(&mut self) -> PResult<math::Operation> {
use math::{Operation, Unary};
/// Parses a [unary operation](Operation::Unary) expression
fn unary(&mut self) -> PResult<Operation> {
let mut operators = vec![];
while let Ok(op) = self.unary_op() {
operators.push(op)
@ -584,15 +666,15 @@ impl Parser {
operand: self.primary_operation()?.into(),
}))
}
/// Parses a [primary operation](math::Operation::Primary) expression
fn primary_operation(&mut self) -> PResult<math::Operation> {
Ok(math::Operation::Call(self.call()?))
/// Parses a [primary operation](Operation::Primary) expression
fn primary_operation(&mut self) -> PResult<Operation> {
Ok(Operation::Call(self.call()?))
}
}
macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) {
$($(#[$m])* fn $f(&mut self) -> PResult<operator::Binary> {
use operator::Binary;
let token = self.peek()?;
let token = self.peek().wrap(Error::not_operator())?;
let out = Ok(match token.ty() {
$($type => Binary::$op,)*
_ => Err(Error::not_operator().token(token.clone()))?,
@ -689,9 +771,8 @@ impl Parser {
}
/// # [Control Flow](control)
impl Parser {
/// Parses a [control flow](control::Flow) expression
fn flow(&mut self) -> PResult<control::Flow> {
use control::Flow;
/// Parses a [control flow](Flow) expression
fn flow(&mut self) -> PResult<Flow> {
use Keyword::{Break, Continue, For, If, Return, While};
let token = self.peek()?;
match token.ty() {
@ -703,55 +784,47 @@ impl Parser {
Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue),
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
fn parse_if(&mut self) -> PResult<control::If> {
/// Parses an [if](If) expression
fn parse_if(&mut self) -> PResult<If> {
self.keyword(Keyword::If)?;
Ok(control::If {
cond: self.expr()?.into(),
body: self.block()?,
else_: self.parse_else()?,
})
Ok(If { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? })
}
/// Parses a [while](control::While) expression
fn parse_while(&mut self) -> PResult<control::While> {
/// Parses a [while](While) expression
fn parse_while(&mut self) -> PResult<While> {
self.keyword(Keyword::While)?;
Ok(control::While {
cond: self.expr()?.into(),
body: self.block()?,
else_: self.parse_else()?,
})
Ok(While { cond: self.expr()?.into(), body: self.block()?, else_: self.parse_else()? })
}
/// Parses a [for](control::For) expression
fn parse_for(&mut self) -> PResult<control::For> {
/// Parses a [for](For) expression
fn parse_for(&mut self) -> PResult<For> {
self.keyword(Keyword::For)?;
Ok(control::For {
Ok(For {
var: self.identifier()?,
iter: { self.keyword(Keyword::In)?.expr()?.into() },
body: self.block()?,
else_: self.parse_else()?,
})
}
/// Parses an [else](control::Else) sub-expression
fn parse_else(&mut self) -> PResult<Option<control::Else>> {
/// Parses an [else](Else) sub-expression
fn parse_else(&mut self) -> PResult<Option<Else>> {
// it's fine for `else` to be missing entirely
self.keyword(Keyword::Else)
.ok()
.map(|p| Ok(control::Else { expr: p.expr()?.into() }))
.map(|p| Ok(Else { expr: p.expr()?.into() }))
.transpose()
}
/// Parses a [break](control::Break) expression
fn parse_break(&mut self) -> PResult<control::Break> {
Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() })
/// Parses a [break](Break) expression
fn parse_break(&mut self) -> PResult<Break> {
Ok(Break { expr: self.keyword(Keyword::Break)?.expr()?.into() })
}
/// Parses a [return](control::Return) expression
fn parse_return(&mut self) -> PResult<control::Return> {
Ok(control::Return { expr: self.keyword(Keyword::Return)?.expr()?.into() })
/// Parses a [return](Return) expression
fn parse_return(&mut self) -> PResult<Return> {
Ok(Return { expr: self.keyword(Keyword::Return)?.expr()?.into() })
}
/// Parses a [continue](control::Continue) expression
fn parse_continue(&mut self) -> PResult<control::Continue> {
/// Parses a [continue](Continue) expression
fn parse_continue(&mut self) -> PResult<Continue> {
self.keyword(Keyword::Continue)?;
Ok(control::Continue)
Ok(Continue)
}
}

View File

@ -1,4 +1,5 @@
//! A [Printer] pretty-prints a Conlang [syntax tree](crate::ast)
use super::ast::preamble::*;
use std::{
fmt::Display,
@ -7,17 +8,14 @@ use std::{
/// Prettily prints this node
pub trait PrettyPrintable {
/// 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) {
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<()> {
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)
@ -26,6 +24,18 @@ pub struct Printer<W: Write> {
level: u32,
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>> {
fn default() -> Self {
Self { level: 0, writer: stdout().lock() }
@ -67,82 +77,154 @@ impl<W: Write> Printer<W> {
macro visit_operator($self:ident.$op:expr) {
$self.space()?.put($op)?.space().map(drop)
}
impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
fn visit_program(&mut self, prog: &Program) -> IOResult<()> {
// delegate to the walker
for stmt in &prog.0 {
self.visit_statement(stmt)?;
impl PrettyPrintable for Start {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
let Self(program) = self;
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(())
}
fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> {
match stmt {
Stmt::Let(stmt) => self.visit_let(stmt)?,
Stmt::Fn(function) => self.visit_fn_decl(function)?,
Stmt::Expr(e) => {
self.visit_expr(e)?;
self.put(';').map(drop)?
}
impl PrettyPrintable for Stmt {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
match self {
Stmt::Let(value) => value.visit(p),
Stmt::Fn(value) => value.visit(p),
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;
self.put("let")?.space()?;
}
impl PrettyPrintable for Let {
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 {
self.put("mut")?.space()?;
p.put("mut")?.space()?;
}
self.visit_identifier(name)?;
name.visit(p)?;
if let Some(ty) = ty {
self.put(':')?.space()?.visit_identifier(ty)?;
ty.visit(p.put(':')?.space()?)?
}
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<()> {
let FnDecl { name, args, body } = function;
self.put("fn")?.space()?;
self.visit_identifier(name)?;
self.space()?.put('(')?;
}
impl PrettyPrintable for FnDecl {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
let FnDecl { name, args, body } = self;
p.put("fn")?.space()?;
name.visit(p)?;
p.space()?.put('(')?;
for (idx, arg) in args.iter().enumerate() {
if idx > 0 {
self.put(',')?.space()?;
p.put(',')?.space()?;
}
self.visit_identifier(arg)?;
arg.visit(p)?;
}
self.put(')')?.space()?;
self.visit_block(body)
p.put(')')?.space()?;
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<()> {
let math::Assign { target, operator, init } = assign;
self.visit_identifier(target)?;
self.visit_assign_op(operator)?;
self.visit_operation(init)
impl PrettyPrintable for Operation {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
match self {
Operation::Assign(value) => value.visit(p),
Operation::Binary(value) => value.visit(p),
Operation::Unary(value) => value.visit(p),
Operation::Call(value) => value.visit(p),
}
fn visit_binary(&mut self, binary: &math::Binary) -> IOResult<()> {
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)
}
impl PrettyPrintable for Assign {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
let Assign { target, operator, init } = self;
target.visit(p)?;
operator.visit(p)?;
init.visit(p)
}
fn visit_unary(&mut self, unary: &math::Unary) -> IOResult<()> {
let math::Unary { operators, operand } = unary;
for op in operators {
self.visit_unary_op(op)?;
}
self.visit_operation(operand)
}
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;
visit_operator!(self.match op {
visit_operator!(p.match self {
Assign::Assign => "=",
Assign::AddAssign => "+=",
Assign::SubAssign => "-=",
@ -156,9 +238,23 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
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;
visit_operator!(self.match op {
visit_operator!(p.match self {
Binary::Mul => "*",
Binary::Div => "/",
Binary::Rem => "%",
@ -182,9 +278,20 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
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;
self.put(match op {
p.put(match self {
Unary::RefRef => "&&",
Unary::Deref => "*",
Unary::Ref => "&",
@ -196,108 +303,155 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
})
.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<()> {
self.put(&ident.0).map(drop)
impl PrettyPrintable for Call {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
match self {
Call::FnCall(value) => value.visit(p),
Call::Primary(value) => value.visit(p),
}
fn visit_string_literal(&mut self, string: &str) -> IOResult<()> {
self.put("\"")?.put(string)?.put("\"").map(drop)
}
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<()> {
for (idx, expr) in tuple.elements.iter().enumerate() {
if idx > 0 {
self.put(',')?.space()?;
}
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)?;
}
impl PrettyPrintable for FnCall {
fn visit<W: Write>(&self, p: &mut Printer<W>) -> IOResult<()> {
let FnCall { callee, args } = self;
callee.visit(p)?;
for arg_list in args {
self.put('(')?;
self.visit_tuple(arg_list)?;
self.put(')')?;
p.put('(')?;
arg_list.visit(p)?;
p.put(')')?;
}
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()?)
}
}

View File

@ -87,6 +87,9 @@ pub enum Keyword {
Let,
Mut,
Return,
SelfKw,
SelfTy,
Super,
True,
While,
}
@ -174,6 +177,9 @@ impl Display for Keyword {
Self::Let => "let".fmt(f),
Self::Mut => "mut".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::While => "while".fmt(f),
}
@ -195,6 +201,9 @@ impl FromStr for Keyword {
"let" => Self::Let,
"mut" => Self::Mut,
"return" => Self::Return,
"self" => Self::SelfKw,
"Self" => Self::SelfTy,
"super" => Self::Super,
"true" => Self::True,
"while" => Self::While,
_ => Err(())?,

View File

@ -1,8 +1,7 @@
// Calculate Fibonacci numbers
fn main() -> i128 {
print("fib(10):");
fib(10)
print("fib(10): ", fib(10));
}
/// Implements the classic recursive definition of fib()