Skip to content

Commit

Permalink
Merge branch 'master' into feature/improve-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
CohenArthur committed Nov 29, 2021
2 parents 0c4cfe4 + fe0b802 commit 55d8851
Show file tree
Hide file tree
Showing 23 changed files with 210 additions and 147 deletions.
1 change: 1 addition & 0 deletions .github/bors.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
status = ["clippy", "build", "build-without-warnings","tests", "functional_tests" ]
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ libloading = "0.7"

[dev-dependencies]
libc = "0.2"

[[bin]]
name = "jinko"
path = "interpreter/jinko.rs"
50 changes: 50 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,53 @@ func add_friend(name: Name, f_name: FirstName, n_name: Nickname) -> Friend {
/* Some code */
}
```
## Generics
Generics rely on the use of brackets so that the parser can *easily* differentiate between
a generic use/declaration (`A<T>`) versus a variable comparison (`A < T`) without any sort
of lookahead. Thus, the following in C++
```c++
std::vector<std::unordered_map<std::string, int>> map = ...;
```
would become in jinko
```rust
map: Vec[Map[string, int]] = ...;
```
Likewise, the following Rust code
```rust
fn takes_result(r: Result<String, SomeErrorType>) {}
```
becomes
```rust
func takes_result(r: Result[string, SomeErrorType]) {}
```
This syntax is similar to scala's generics, which are quite nice to use.
Generics, just like function parameters, should be able to take a default value, which
could be specified by name.
```rust
type BaseError(inner: string);
type Ok[T](T);
type Err[T](T);
type Result[T = void, E = BaseError](Ok[T], Err[E]);
// We can avoid typing `Result` by using type promotion but w/e, this is for
// the example's sake
ok_0 = Result[int](Ok(15)); // E defaults to BaseError
ok_1 = Result[int, string](Ok(15));
ok_2 = Result[int, string](Err("oops"));
ok_4 = Result[T = int, string](Err("oops"));
ok_5 = Result[T = int, E = string](Err("oops"));
ok_6 = Result[E = bool](Err(false)); // T defaults to void
```
20 changes: 20 additions & 0 deletions SYNTAX.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,23 @@ x = while boolean_condition -> int {
i++;
} // x now equals 12
```

## Generics

In order to keep the parser simple, generics use brackets instead of chevrons.

```rust
type Nothing;
type Some[T](T);
type Maybe[T](Nothing | Some[T]);

func takes_maybe[SomeTyName](m: Maybe[SomeTyName]) -> SomeTyName {}

type Ok[T](T);
type Err[T](T);
type Result[T = void, E = Error](Ok[T] | Err[E]);

func returns_result() -> Result[int, SomeErrorType] {}

type Map[K = string, V](...);
```
8 changes: 8 additions & 0 deletions src/args.rs → interpreter/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub struct Args {
#[structopt(short, long)]
interactive: bool,

#[structopt(long = "no-std")]
nostdlib: bool,

#[structopt(short, long)]
debug: bool,

Expand Down Expand Up @@ -52,6 +55,11 @@ impl Args {
self.debug
}

/// Is the context launched without stdlib
pub fn nostdlib(&self) -> bool {
self.nostdlib
}

/// Arguments given to the program
pub fn project_args(&self) -> Vec<String> {
self.arguments.clone()
Expand Down
43 changes: 17 additions & 26 deletions src/main.rs → interpreter/jinko.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
#[warn(missing_docs)]
// FIXME: Add #![warn(missing_docs)]

mod args;
mod builtins;
mod context;
mod error;
mod ffi;
mod indent;
mod instance;
mod instruction;
mod parser;
mod repl;
mod typechecker;
mod utils;
mod value;

use jinko::{
CheckedType, Context, Error, FromObjectInstance, JkBool, JkFloat, JkInt, ObjectInstance,
};

use args::Args;
use parser::Parser;
use repl::Repl;
use std::{fs, path::Path};

pub use builtins::Builtins;
pub use context::{Context, Scope, ScopeMap};
pub use error::{ErrKind, Error};
pub use indent::Indent;
pub use instance::{FromObjectInstance, ObjectInstance, ToObjectInstance};
pub use instruction::{InstrKind, Instruction};
pub use typechecker::{CheckedType, TypeCheck, TypeCtx};
pub use value::{JkBool, JkChar, JkConstant, JkFloat, JkInt, JkString, Value};

// FIXME: Add documentation
pub type InteractResult = Result<(Option<ObjectInstance>, Context), Error>;

Expand All @@ -42,10 +26,10 @@ fn handle_exit_code(result: Option<ObjectInstance>) -> ! {
// If it's an expression, return if you can (if it's an int)
Some(i) => match i.ty() {
CheckedType::Resolved(ty) => match ty.id() {
"int" => exit(JkInt::from_instance(&i).0 as i32),
"float" => exit(JkFloat::from_instance(&i).0 as i32),
"int" => exit(JkInt::from_instance(&i).rust_value() as i32),
"float" => exit(JkFloat::from_instance(&i).rust_value() as i32),
"bool" => {
let b_value = JkBool::from_instance(&i).0;
let b_value = JkBool::from_instance(&i).rust_value();
match b_value {
true => exit(0),
false => exit(1),
Expand All @@ -62,7 +46,14 @@ fn handle_exit_code(result: Option<ObjectInstance>) -> ! {
fn handle_input(args: &Args, file: &Path) -> InteractResult {
let input = fs::read_to_string(file)?;

let mut ctx = Parser::parse(&input)?;
let mut ctx = Context::new();

if !args.nostdlib() {
ctx.init_stdlib()?;
}

jinko::parse(&mut ctx, &input)?;

ctx.set_path(Some(file.to_owned()));
ctx.set_args(args.project_args());
ctx.set_debug(args.debug());
Expand Down
36 changes: 17 additions & 19 deletions src/repl.rs → interpreter/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@ mod prompt;
use prompt::Prompt;
use std::path::PathBuf;

use jinko::{CheckedType, TypeCheck, TypeCtx};
use jinko::{
Context, Error, FromObjectInstance, Instruction, JkConstant, ObjectInstance, constructs
};

use crate::{Args, InteractResult};

use linefeed::{DefaultTerminal, Interface, ReadResult};

use crate::args::Args;
use crate::typechecker::{CheckedType, TypeCheck, TypeCtx};
use crate::{
parser::constructs, Context, Error, FromObjectInstance, Instruction, InteractResult,
JkConstant, ObjectInstance,
};
struct ReplInstance(ObjectInstance);

// FIXME:
// - Is Display really how we want to go about it?
// - Cleanup the code
// - This should not be here
impl std::fmt::Display for ObjectInstance {
impl std::fmt::Display for ReplInstance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.ty() {
match self.0.ty() {
CheckedType::Resolved(ty) => match ty.id() {
"int" => JkConstant::<i64>::from_instance(self).print(),
"float" => JkConstant::<f64>::from_instance(self).print(),
"char" => JkConstant::<char>::from_instance(self).print(),
"string" => JkConstant::<String>::from_instance(self).print(),
"bool" => JkConstant::<bool>::from_instance(self).print(),
_ => self.as_string(),
"int" => JkConstant::<i64>::from_instance(&self.0).print(),
"float" => JkConstant::<f64>::from_instance(&self.0).print(),
"char" => JkConstant::<char>::from_instance(&self.0).print(),
"string" => JkConstant::<String>::from_instance(&self.0).print(),
"bool" => JkConstant::<bool>::from_instance(&self.0).print(),
_ => self.0.as_string(),
},
_ => format!(""),
}
Expand Down Expand Up @@ -121,7 +119,7 @@ impl<'args> Repl<'args> {
// }

if let Some(result) = inst.execute(&mut ctx) {
println!("{}", result);
println!("{}", ReplInstance(result));
};

ctx.emit_errors();
Expand Down
2 changes: 1 addition & 1 deletion src/repl/prompt.rs → interpreter/repl/prompt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Creates a prompt based on the context's current status
use crate::context::Context;
use colored::Colorize;
use jinko::Context;

pub struct Prompt;

Expand Down
30 changes: 13 additions & 17 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,8 @@ impl Context {
ep
}

/// Create a new context, including the standard library
pub fn new() -> Context {
let mut ctx = Context::empty();

// Include the standard library
let mut stdlib_incl =
crate::instruction::Incl::new(String::from("stdlib"), Some(String::from("")));
stdlib_incl.set_base(PathBuf::new());
// FIXME: Remove unwrap
ctx.entry_point
.add_instruction(Box::new(stdlib_incl))
.unwrap();

ctx
}

/// Create a new empty context without the standard library
pub fn empty() -> Context {
pub fn new() -> Context {
let mut ctx = Context {
debug_mode: false,
entry_point: Self::new_entry(),
Expand Down Expand Up @@ -353,6 +337,17 @@ impl Context {
pub fn libs(&self) -> &Vec<libloading::Library> {
&self.external_libs
}

/// Includes the standard library in the context
pub fn init_stdlib(&mut self) -> Result<(), Error> {
let mut stdlib_incl =
crate::instruction::Incl::new(String::from("stdlib"), Some(String::from("")));
stdlib_incl.set_base(PathBuf::new());

self.entry_point.add_instruction(Box::new(stdlib_incl))?;

Ok(())
}
}

/// Printer for the context's usage of the ScopeMap
Expand Down Expand Up @@ -414,6 +409,7 @@ mod tests {
#[test]
fn t_print_scopemap() {
let mut ctx = Context::new();
ctx.init_stdlib().unwrap();
ctx.execute().unwrap();

let output = format!("{}", ctx.scope_map);
Expand Down
4 changes: 2 additions & 2 deletions src/instruction/if_else.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! `IfElse`s are used to represent an if/else statement in the source code. They have
//! a condition, a body and an optional else body.
//!
//! ```
//! ```ignore
//! if condition {
//! condition_evaluates_to_true();
//! } else {
Expand All @@ -11,7 +11,7 @@
//!
//! They can be used to return values, just like you would with any block.
//!
//! ```
//! ```ignore
//! x = if condition { 12 } else { 13 };
//! ```
Expand Down
4 changes: 2 additions & 2 deletions src/instruction/jk_return.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Return construct is used to return early from a function
//! ```
//! ```ignore
//! return
//! ```
//!
//! It can be used to return values
//!
//! ```
//! ```ignore
//! return 42
//! ```
Expand Down
2 changes: 1 addition & 1 deletion src/instruction/loop_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ mod tests {

#[test]
fn valid_for_block() {
let ctx = jinko! {
let _ctx = jinko! {
mut counter = 0;
for i in range(0, 15) {
counter = counter + 1;
Expand Down
4 changes: 2 additions & 2 deletions src/instruction/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//!
//! For some simple constructs, this simply means renaming self's name, such as a TypeDec:
//!
//! ```
//! ```ignore
//! // some_type.jk
//! type SomeType(...);
//!
Expand All @@ -28,7 +28,7 @@
//! ```
//!
//! Other instructions also have owernship of other instructions, such as blocks:
//! ```
//! ```ignore
//! // source.jk
//! { // block enter
//! type InBlock(...);
Expand Down
2 changes: 1 addition & 1 deletion src/instruction/var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl Instruction for Var {
base = format!("{} /* : {} */", base, ty.id());
}

format!("{} = {}", base, self.instance)
format!("{} = {}", base, self.instance.as_string())
}

fn as_bool(&self, ctx: &mut Context) -> Option<bool> {
Expand Down
23 changes: 23 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// FIXME: Make crate attribute `#![warn(missing_docs)]`

mod builtins;
mod context;
mod error;
mod ffi;
mod indent;
mod instance;
mod instruction;
mod parser;
mod typechecker;
mod utils;
mod value;

pub use builtins::Builtins;
pub use context::{Context, Scope, ScopeMap};
pub use error::{ErrKind, Error};
pub use indent::Indent;
pub use instance::{FromObjectInstance, ObjectInstance, ToObjectInstance};
pub use instruction::{InstrKind, Instruction};
pub use parser::{parse, constructs};
pub use typechecker::{CheckedType, TypeCheck, TypeCtx};
pub use value::{JkBool, JkChar, JkConstant, JkFloat, JkInt, JkString, Value};
Loading

0 comments on commit 55d8851

Please sign in to comment.