-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0a70ca6
commit 1dc80b5
Showing
7 changed files
with
409 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "ook" | ||
version = "0.1.0" | ||
authors = [ "Patrick Winters <19wintersp@gmail.com>" ] | ||
edition = "2018" | ||
description = "A basic, customisable Ook interpreter" | ||
|
||
[dependencies] | ||
clap = "2.33.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use std::process; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Error { | ||
message: String, | ||
pos: (usize, usize), | ||
line: String, | ||
} | ||
|
||
impl Error { | ||
pub fn new(message: String, source: String, pos: (usize, usize)) -> Self { | ||
Self { | ||
message, | ||
pos, | ||
line: source.split("\n").nth(pos.1).unwrap().into(), | ||
} | ||
} | ||
|
||
pub fn exit(self) -> ! { | ||
self.display(); | ||
process::exit(2); | ||
} | ||
|
||
pub fn display(self) { | ||
let numl = ((self.pos.1 + 1) as f32).log10() as usize + 1; | ||
|
||
eprintln!("{}\n", self.message); | ||
eprintln!(" {} | {}", self.pos.1 + 1, self.line); | ||
eprintln!("{}^ here", "-".repeat(numl + self.pos.0 + 4)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use crate::error::Error; | ||
use crate::{ Token, TokenType }; | ||
|
||
pub fn interpret(tokens: Vec<Token>, source: String) -> Result<(), Error> { | ||
let len = tokens.len(); | ||
|
||
let mut mem = [ 0u8; 65536 ]; | ||
let mut ptr = 0usize; | ||
|
||
let mut ip = 0usize; | ||
|
||
let mut loops = Vec::<usize>::new(); | ||
|
||
loop { | ||
if ip >= len { break } | ||
|
||
match tokens[ip].token { | ||
TokenType::PtrLeft => ptr -= 1, | ||
TokenType::PtrRight => ptr += 1, | ||
TokenType::Increment => mem[ptr] += 1, | ||
TokenType::Decrement => mem[ptr] -= 1, | ||
TokenType::Output => print!("{}", mem[ptr] as char), | ||
TokenType::Input => unimplemented!(), | ||
TokenType::LoopStart => { | ||
if mem[ptr] == 0 { | ||
let start = tokens[ip].pos; | ||
|
||
loop { | ||
ip += 1; | ||
|
||
if ip >= len { | ||
return Err(Error::new("Error: Unmatched loop start".into(), source, start)) | ||
} | ||
|
||
if tokens[ip].token == TokenType::LoopEnd { | ||
break | ||
} | ||
} | ||
} else { | ||
loops.push(ip); | ||
} | ||
}, | ||
TokenType::LoopEnd => { | ||
if let Some(pos) = loops.last() { | ||
if mem[ptr] != 0 { | ||
ip = pos + 1; | ||
continue | ||
} | ||
} else { | ||
return Err(Error::new("Error: Unmatched loop end".into(), source, tokens[ip].pos)) | ||
} | ||
}, | ||
} | ||
|
||
ip += 1; | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
mod error; | ||
mod interpreter; | ||
mod token; | ||
mod tokeniser; | ||
|
||
use interpreter::interpret; | ||
use token::{ Token, TokenType }; | ||
use tokeniser::{ Config, tokenise }; | ||
|
||
use std::fs; | ||
|
||
use clap::{ clap_app, Error }; | ||
|
||
static TOKEN_A: &str = "Ook."; | ||
static TOKEN_B: &str = "Ook!"; | ||
static TOKEN_C: &str = "Ook?"; | ||
|
||
fn main() { | ||
let matches = clap_app!(app => | ||
(name: env!("CARGO_PKG_NAME")) | ||
(version: env!("CARGO_PKG_VERSION")) | ||
(author: env!("CARGO_PKG_AUTHORS")) | ||
(about: env!("CARGO_PKG_DESCRIPTION")) | ||
(@arg csin: -i --("case-insensitive") "Ignores case when matching tokens") | ||
(@arg strict: -s --strict "Enables strict mode: only tokens are permitted") | ||
(@arg token_a: -a --("token-a") +takes_value "Specify custom A token") | ||
(@arg token_b: -b --("token-b") +takes_value "Specify custom B token") | ||
(@arg token_c: -c --("token-c") +takes_value "Specify custom C token") | ||
(@arg eval: -e --eval +takes_value "Specify script to execute instead of FILE") | ||
(@arg FILE: +takes_value required_unless[eval] "File to execute") | ||
).get_matches(); | ||
|
||
let strict = matches.is_present("strict"); | ||
let insensitive = matches.is_present("csin"); | ||
|
||
let token_a = matches.value_of("token_a").unwrap_or(TOKEN_A).into(); | ||
let token_b = matches.value_of("token_b").unwrap_or(TOKEN_B).into(); | ||
let token_c = matches.value_of("token_c").unwrap_or(TOKEN_C).into(); | ||
|
||
let code = if let Some(file) = matches.value_of("FILE") { | ||
match fs::read_to_string(file) { | ||
Ok(data) => data, | ||
Err(err) => { | ||
let err: Error = err.into(); | ||
err.exit(); | ||
}, | ||
} | ||
} else { | ||
matches.value_of("eval").unwrap().into() | ||
}; | ||
|
||
let config = Config { | ||
tokens: (token_a, token_b, token_c), | ||
strict, | ||
insensitive, | ||
}; | ||
|
||
let tokens = tokenise(code.clone(), config) | ||
.map_err(|err| err.exit()) | ||
.unwrap(); | ||
let _ = interpret(tokens, code) | ||
.map_err(|err| err.exit()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Token { | ||
pub token: TokenType, | ||
pub pos: (usize, usize), | ||
} | ||
|
||
impl Token { | ||
pub fn new(token: TokenType, pos: (usize, usize)) -> Self { | ||
Self { token, pos } | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq)] | ||
pub enum TokenType { | ||
PtrRight, | ||
PtrLeft, | ||
Increment, | ||
Decrement, | ||
Output, | ||
Input, | ||
LoopStart, | ||
LoopEnd, | ||
} |
Oops, something went wrong.