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]
|
||||
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
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 *)
|
||||
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 *)
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()?)
|
||||
}
|
||||
}
|
||||
|
@ -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(())?,
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user