Skip to content
This repository has been archived by the owner on Jun 3, 2021. It is now read-only.

[WIP] Add a proper REPL #464

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ color-backtrace = { version = "0.4", default-features = false, optional = true }
counter = "0.4"
atty = { version = "0.2", default-features = false, optional = true }
git-testament = { version = "0.1", optional = true }
rustyline = { version = "6.1.2", optional = true }
rustyline-derive = { version = "0.3.1", optional = true }

[dev-dependencies]
env_logger = { version = "0.7", default-features = false }
Expand All @@ -48,19 +50,25 @@ proptest = "^0.9.6"
proptest-derive = "0.1"

[features]
default = ["cc", "codegen", "color-backtrace"]
default = ["cc", "codegen", "color-backtrace", "repl"]
# The `swcc` binary
cc = ["ansi_term", "git-testament", "tempfile", "pico-args", "codegen", "atty"]
codegen = ["cranelift", "cranelift-module", "cranelift-object"]
jit = ["codegen", "cranelift-simplejit"]
repl = ["rustyline", "rustyline-derive", "jit"]
# for internal use
_test_headers = []

[[bin]]
name = "swcc"
path = "src/main.rs"
path = "src/bin/swcc.rs"
required-features = ["cc"]

[[bin]]
name = "swcci"
path = "src/bin/swcci.rs"
required-features = ["repl"]

[[bench]]
name = "examples"
harness = false
Expand Down
1 change: 0 additions & 1 deletion src/analyze/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,6 @@ impl Qualifiers {
#[cfg(test)]
mod test {
use super::*;
use crate::analyze::test::analyze;
use crate::analyze::*;
pub(crate) fn expr(input: &str) -> CompileResult<Expr> {
analyze(input, Parser::expr, PureAnalyzer::expr)
Expand Down
46 changes: 24 additions & 22 deletions src/analyze/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use counter::Counter;

use crate::data::{error::Warning, hir::*, lex::Keyword, *};
use crate::intern::InternedStr;
use crate::parse::{Lexer, Parser};
use crate::lex::PreProcessor;
use crate::parse::{parser, Lexer, Parser};
use crate::RecursionGuard;

pub(crate) type TagScope = Scope<InternedStr, TagEntry>;
Expand Down Expand Up @@ -1051,6 +1052,7 @@ impl PureAnalyzer {
/// This returns an opaque index to the `Metadata`.
fn declare(&mut self, mut decl: Variable, init: bool, location: Location) -> Symbol {
if decl.id == "main".into() {
println!("main: {:?}", decl);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
println!("main: {:?}", decl);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ups 😅

if let Type::Function(ftype) = &decl.ctype {
// int main(int)
if !ftype.is_main_func_signature() {
Expand Down Expand Up @@ -1343,33 +1345,33 @@ impl UnitSpecifier {
}
}

/// Analyzes the given input using the given parser, and analyze function.
pub fn analyze<'c, 'input: 'c, P, A, R, S, E>(
input: &'input str,
parse_func: P,
analyze_func: A,
) -> CompileResult<R>
where
P: Fn(&mut Parser<PreProcessor<'c>>) -> Result<S, E>,
A: Fn(&mut PureAnalyzer, S) -> R,
CompileError: From<E>,
{
let mut p = parser(input);
let ast = parse_func(&mut p)?;
let mut a = PureAnalyzer::new();
let e = analyze_func(&mut a, ast);
if let Some(err) = a.error_handler.pop_front() {
return Err(err);
}
Ok(e)
}

#[cfg(test)]
pub(crate) mod test {
use super::{Error, *};
use crate::data::types::{ArrayType, FunctionType, Type::*};
use crate::lex::PreProcessor;
use crate::parse::test::*;

pub(crate) fn analyze<'c, 'input: 'c, P, A, R, S, E>(
input: &'input str,
parse_func: P,
analyze_func: A,
) -> CompileResult<R>
where
P: Fn(&mut Parser<PreProcessor<'c>>) -> Result<S, E>,
A: Fn(&mut PureAnalyzer, S) -> R,
CompileError: From<E>,
{
let mut p = parser(input);
let ast = parse_func(&mut p)?;
let mut a = PureAnalyzer::new();
let e = analyze_func(&mut a, ast);
if let Some(err) = a.error_handler.pop_front() {
return Err(err);
}
Ok(e)
}

fn maybe_decl(s: &str) -> Option<CompileResult<Declaration>> {
decls(s).into_iter().next()
}
Expand Down
2 changes: 1 addition & 1 deletion src/analyze/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ impl FunctionAnalyzer<'_> {
#[cfg(test)]
mod tests {
use super::*;
use crate::analyze::test::{analyze, analyze_expr};
use crate::analyze::FunctionData;
use crate::analyze::{analyze, test::analyze_expr};
use crate::data::*;
use crate::Parser;

Expand Down
49 changes: 49 additions & 0 deletions src/bin/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ColorChoice {
Always,
Auto,
Never,
}

impl ColorChoice {
pub(super) fn use_color_for(self, stream: atty::Stream) -> bool {
match self {
ColorChoice::Always => true,
ColorChoice::Never => false,
ColorChoice::Auto => atty::is(stream),
}
}
}

impl std::str::FromStr for ColorChoice {
type Err = &'static str;
fn from_str(s: &str) -> Result<ColorChoice, &'static str> {
match s {
"always" => Ok(ColorChoice::Always),
"auto" => Ok(ColorChoice::Auto),
"never" => Ok(ColorChoice::Never),
_ => Err("Invalid color choice"),
}
}
}

#[cfg(feature = "color-backtrace")]
pub(super) mod backtrace {
use super::ColorChoice;
use color_backtrace::termcolor::{self, StandardStream};
use color_backtrace::BacktracePrinter;

impl Into<termcolor::ColorChoice> for ColorChoice {
fn into(self) -> termcolor::ColorChoice {
match self {
ColorChoice::Always => termcolor::ColorChoice::Always,
ColorChoice::Auto => termcolor::ColorChoice::Auto,
ColorChoice::Never => termcolor::ColorChoice::Never,
}
}
}

pub(crate) fn install(color: ColorChoice) {
BacktracePrinter::new().install(Box::new(StandardStream::stderr(color.into())));
}
}
37 changes: 37 additions & 0 deletions src/bin/repl/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use saltwater::{analyze, Parser, PureAnalyzer};
use std::fmt;

const HELP_MESSAGE: &'static str = "
:help Shows this message
:quit Quits the repl
:type Shows the type of the given expression
";

#[derive(Debug)]
pub(super) enum CommandError {
CompileError(saltwater::CompileError),
}

impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CommandError::CompileError(err) => writeln!(f, "{}", err.data),
}
}
}

pub(super) fn show_type(code: String) -> Result<(), CommandError> {
let ast = analyze(&code, Parser::expr, PureAnalyzer::expr)
.map_err(|e| CommandError::CompileError(e))?;
println!("Type: {}", ast.ctype);
Ok(())
}

pub(super) fn print_help(_code: String) -> Result<(), CommandError> {
println!("{}", HELP_MESSAGE);
Ok(())
}

pub(super) fn quit_repl(_code: String) -> Result<(), CommandError> {
std::process::exit(0)
}
96 changes: 96 additions & 0 deletions src/bin/repl/helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use super::COMMANDS;
use rustyline::{
completion::{Completer, Pair},
error::ReadlineError,
highlight::{Highlighter, MatchingBracketHighlighter},
hint::Hinter,
validate::{self, MatchingBracketValidator, Validator},
Context,
};
use rustyline_derive::Helper;
use std::borrow::Cow;

#[derive(Helper)]
pub(super) struct ReplHelper {
pub(super) highlighter: MatchingBracketHighlighter,
pub(super) validator: MatchingBracketValidator,
pub(super) hinter: CommandHinter,
}

impl Completer for ReplHelper {
type Candidate = Pair;

fn complete(
&self,
_line: &str,
_pos: usize,
_ctx: &Context<'_>,
) -> Result<(usize, Vec<Pair>), ReadlineError> {
Ok((0, vec![]))
}
}

impl Hinter for ReplHelper {
fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
self.hinter.hint(line, pos, ctx)
}
}

impl Highlighter for ReplHelper {
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
&'s self,
prompt: &'p str,
_default: bool,
) -> Cow<'b, str> {
Cow::Borrowed(prompt)
}

fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
let style = ansi_term::Style::new().dimmed();
Cow::Owned(style.paint(hint).to_string())
}

fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
self.highlighter.highlight(line, pos)
}

fn highlight_char(&self, line: &str, pos: usize) -> bool {
self.highlighter.highlight_char(line, pos)
}
}

impl Validator for ReplHelper {
fn validate(
&self,
ctx: &mut validate::ValidationContext,
) -> rustyline::Result<validate::ValidationResult> {
self.validator.validate(ctx)
}

fn validate_while_typing(&self) -> bool {
self.validator.validate_while_typing()
}
}

pub(super) struct CommandHinter;

impl Hinter for CommandHinter {
fn hint(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option<String> {
if pos < line.len() {
return None;
}
let start = &line[..pos];
if !start.starts_with(":") {
return None;
}
let start = &start[1..];

COMMANDS
.iter()
.filter(|(k, _v)| k.starts_with(&start[..]))
.map(|(k, _v)| k)
.next()
.map(|s| &s[start.len()..])
.map(|s| s.to_string())
}
}
Loading