Grand renaming of things

This commit is contained in:
greg 2017-08-29 00:28:19 -07:00
parent 801896bcc6
commit dd22ca0291
18 changed files with 243 additions and 223 deletions

View File

@ -1,7 +1,11 @@
Schala
======
## The experimental programming language meta-interpreter TODO:
-null-only language should be called Maaru
-haskell-ish langauge should be called Robo
-typeful scripting language should be called schala
rename accordingly!
# Schala - a programming language meta-interpreter
Schala is a Rust-language framework written to make it easy to Schala is a Rust-language framework written to make it easy to
create and experiment with toy programming languages. It provides create and experiment with toy programming languages. It provides
@ -10,34 +14,47 @@ for tokenizing text, parsing tokens, evaluating an abstract syntax tree,
and other tasks that are common to all programming languages. and other tasks that are common to all programming languages.
Schala started out life as an experiment in writing a Javascript-like Schala started out life as an experiment in writing a Javascript-like
programming language that would never encounter any kind of runtime programming language that would never encounter any kind of runtime value
value error, but rather always return `null` under any kind of error error, but rather always return `null` under any kind of error condition. I had
condition. I had seen one too many Javascript `Uncaught TypeError: seen one too many Javascript `Uncaught TypeError: Cannot read property ___ of
Cannot read property ___ of undefined` messages, and I was a bit frustrated. undefined` messages, and I was a bit frustrated. Plus I had always wanted to
Plus I had always wanted to write a programming langauge from scratch, write a programming langauge from scratch, and Rust is a fun language to
and Rust is a fun language to program in. program in. Over time I became interested in playing around with other sorts
of programming languages as well, and wanted to make the process as general as
possible.
Over time I became interested in playing around with other sorts The name of the project comes from Schala the Princess of Zeal from the 1995
of programming languages as well, and wanted to make the process SNES RPG *Chrono Trigger*. I like classic JRPGs and enjoyed the thought of
as general as possible. I changed the name of the project to creating a language name confusingly close to Scala. The naming scheme for
Schala, after the Princess of Zeal from *Chrono Trigger*, because I languages implemented with the Schala meta-interpreter is Chrono Trigger
like classic JRPGs and because it sounds sort of like Scala. characters.
Schala is as yet an incomplete personal project that I continue to work ## Languages implemented using the meta-interpreter
on as my time permits.
### Reference works * The eponymous *Schala* language is an interpreted/compiled scripting langauge,
designed to be relatively simple, but with a reasonably sophisticated type
system.
* *Maaru* was the original Schala (since renamed to free up the name *Schala*
for the above language), a very simple dynamically-typed scripting language
such that all possible runtime errors result in null rather than program
failure.
* *Robo* is an experiment in creating a lazy, functional, strongly-typed language
much like Haskell
## Reference works
Here's a partial list of resources I've made use of in the process Here's a partial list of resources I've made use of in the process
of learning how to write a programming language. of learning how to write a programming language.
#### Evaluation ### Evaluation
*Understanding Computation*, Tom Stuart, O'Reilly 2013 *Understanding Computation*, Tom Stuart, O'Reilly 2013
#### Parsing ### Parsing
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
[Crafting Interpreters](http://www.craftinginterpreters.com/) [Crafting Interpreters](http://www.craftinginterpreters.com/)
#### LLVM ### LLVM
http://blog.ulysse.io/2016/07/03/llvm-getting-started.html http://blog.ulysse.io/2016/07/03/llvm-getting-started.html

View File

@ -5,7 +5,7 @@ use std::collections::HashMap;
use self::llvm_sys::prelude::*; use self::llvm_sys::prelude::*;
use self::llvm_sys::{LLVMIntPredicate, LLVMRealPredicate}; use self::llvm_sys::{LLVMIntPredicate, LLVMRealPredicate};
use schala_lang::parser::{AST, Statement, Function, Prototype, Expression, BinOp}; use maaru_lang::parser::{AST, Statement, Function, Prototype, Expression, BinOp};
use language::LLVMCodeString; use language::LLVMCodeString;
use llvm_wrap as LLVMWrap; use llvm_wrap as LLVMWrap;

View File

@ -2,13 +2,13 @@ extern crate take_mut;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use schala_lang::parser::{AST, Statement, Expression, Function, Callable, BinOp}; use maaru_lang::parser::{AST, Statement, Expression, Function, Callable, BinOp};
use std::rc::Rc; use std::rc::Rc;
use std::io::{Write, Stdout, BufWriter}; use std::io::{Write, Stdout, BufWriter};
use std::convert::From; use std::convert::From;
use schala_lang::parser::Expression::*; use maaru_lang::parser::Expression::*;
use schala_lang::parser::Statement::*; use maaru_lang::parser::Statement::*;
type Reduction<T> = (T, Option<SideEffect>); type Reduction<T> = (T, Option<SideEffect>);

View File

@ -1,157 +1,44 @@
extern crate itertools; use ::std::marker::PhantomData;
use self::itertools::Itertools; pub mod tokenizer;
pub mod parser;
pub mod eval;
pub mod compilation;
use language::{ProgrammingLanguage, EvaluationMachine, ParseError, TokenError, LLVMCodeString}; use language::{ProgrammingLanguage, EvaluationMachine, ParseError, TokenError, LLVMCodeString};
pub struct Maaru { pub use self::eval::Evaluator as MaaruEvaluator;
}
impl Maaru { pub struct Maaru<'a> { marker: PhantomData<&'a ()> }
pub fn new() -> Maaru { impl<'a> Maaru<'a> {
Maaru { } pub fn new() -> Maaru <'a> {
Maaru { marker: PhantomData }
} }
} }
pub struct MaaruEvaluator { impl<'a> ProgrammingLanguage for Maaru<'a> {
pub trace_evaluation: bool, type Token = tokenizer::Token;
} type AST = parser::AST;
type Evaluator = MaaruEvaluator<'a>;
#[derive(Debug)]
pub enum Token {
StrLiteral(String),
Backtick,
Newline,
LParen,
RParen,
LBracket,
RBracket,
LBrace,
RBrace,
Period,
Comma,
Colon,
Semicolon,
SingleQuote,
Identifier(String),
Operator(String),
NumLiteral(Number),
}
#[derive(Debug)]
pub enum Number {
IntegerRep(String),
FloatRep(String)
}
pub type AST = Vec<ASTNode>;
#[derive(Debug)]
pub enum ASTNode {
FunctionDefinition(String, Expression),
ImportStatement(String),
}
#[derive(Debug)]
pub enum Expression {
}
impl ProgrammingLanguage for Maaru {
type Token = Token;
type AST = AST;
type Evaluator = MaaruEvaluator;
fn name() -> String { fn name() -> String {
"Maaru".to_string() "Maaru".to_string()
} }
fn tokenize(input: &str) -> Result<Vec<Self::Token>, TokenError> { fn tokenize(input: &str) -> Result<Vec<Self::Token>, TokenError> {
use self::Token::*; tokenizer::tokenize(input)
let mut tokens = Vec::new();
let mut iter = input.chars().peekable();
while let Some(c) = iter.next() {
if c == ';' {
while let Some(c) = iter.next() {
if c == '\n' {
break;
}
}
continue;
}
let cur_tok = match c {
c if char::is_whitespace(c) && c != '\n' => continue,
'\n' => Newline,
'(' => LParen,
')' => RParen,
'[' => LBracket,
']' => RBracket,
'{' => LBrace,
'}' => RBrace,
',' => Comma,
':' => Colon,
';' => Semicolon,
'.' => Period,
'`' => Backtick,
'\'' => SingleQuote,
'"' => {
let mut buffer = String::new();
loop {
match iter.next() {
Some(x) if x == '"' => break,
Some(x) => buffer.push(x),
None => return Err(TokenError::new("Unclosed quote")),
}
}
StrLiteral(buffer)
}
c if c.is_digit(10) => {
let mut integer = true;
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
if let Some(&'.') = iter.peek() {
buffer.push(iter.next().unwrap());
integer = false;
}
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
let inner = if integer {
Number::IntegerRep(buffer)
} else {
Number::FloatRep(buffer)
};
NumLiteral(inner)
},
c if char::is_alphanumeric(c) => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| char::is_alphanumeric(*x)));
Identifier(buffer)
},
c => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| !char::is_whitespace(*x)));
Operator(buffer)
}
};
tokens.push(cur_tok);
}
Ok(tokens)
} }
fn parse(input: Vec<Self::Token>) -> Result<Self::AST, ParseError> {
fn parse(_input: Vec<Self::Token>) -> Result<Self::AST, ParseError> { parser::parse(&input, &[]).map_err(|x| ParseError { msg: x.msg })
Ok(vec!())
} }
fn evaluate(_ast: Self::AST, _evaluator: &mut Self::Evaluator) -> Vec<String> { fn evaluate(ast: Self::AST, evaluator: &mut Self::Evaluator) -> Vec<String> {
vec!["Unimplemented".to_string()] evaluator.run(ast)
} }
fn compile(_ast: Self::AST) -> LLVMCodeString { fn compile(ast: Self::AST) -> LLVMCodeString {
unimplemented!() compilation::compile_ast(ast)
} }
} }
impl EvaluationMachine for MaaruEvaluator { impl<'a> EvaluationMachine for MaaruEvaluator<'a> {
fn set_option(&mut self, option: &str, value: bool) -> bool { fn set_option(&mut self, option: &str, value: bool) -> bool {
if option == "trace_evaluation" { if option == "trace_evaluation" {
self.trace_evaluation = value; self.trace_evaluation = value;
@ -161,9 +48,7 @@ impl EvaluationMachine for MaaruEvaluator {
false false
} }
fn new() -> MaaruEvaluator { fn new() -> MaaruEvaluator<'a> {
MaaruEvaluator { MaaruEvaluator::new(None)
trace_evaluation: false,
}
} }
} }

View File

@ -1,5 +1,5 @@
use schala_lang::tokenizer::{Token, Kw, OpTok}; use maaru_lang::tokenizer::{Token, Kw, OpTok};
use schala_lang::tokenizer::Token::*; use maaru_lang::tokenizer::Token::*;
use std::fmt; use std::fmt;
use std::collections::VecDeque; use std::collections::VecDeque;

View File

@ -9,11 +9,14 @@ use std::process;
use std::io::Write; use std::io::Write;
use std::default::Default; use std::default::Default;
/*
mod schala_lang; mod schala_lang;
use schala_lang::SchalaEvaluator; use schala_lang::SchalaEvaluator;
use schala_lang::Schala; use schala_lang::Schala;
*/
mod maaru_lang; mod maaru_lang;
mod robo_lang;
mod language; mod language;
use language::{ProgrammingLanguage, LanguageInterface, LLVMCodeString, EvaluationMachine}; use language::{ProgrammingLanguage, LanguageInterface, LLVMCodeString, EvaluationMachine};
@ -26,8 +29,8 @@ use virtual_machine::{run_vm, run_assembler};
fn main() { fn main() {
let languages: Vec<Box<LanguageInterface>> = let languages: Vec<Box<LanguageInterface>> =
vec![ vec![
Box::new((Schala::new(), SchalaEvaluator::new(None))), Box::new((maaru_lang::Maaru::new(), maaru_lang::MaaruEvaluator::new(None))),
Box::new((maaru_lang::Maaru::new(), maaru_lang::MaaruEvaluator::new())), Box::new((robo_lang::Robo::new(), robo_lang::RoboEvaluator::new())),
]; ];
let option_matches = let option_matches =
@ -79,7 +82,7 @@ fn main() {
repl.run(); repl.run();
} }
[_, ref filename, _..] => { [_, ref filename, _..] => {
let language = Schala::new(); let language = maaru_lang::Maaru::new();
run_noninteractive(filename, &language, trace_evaluation, compile); run_noninteractive(filename, &language, trace_evaluation, compile);
} }
}; };
@ -308,7 +311,7 @@ pub fn compilation_sequence(llvm_code: LLVMCodeString, sourcefile: &str) {
let obj_filename = "out.o"; let obj_filename = "out.o";
let q: Vec<&str> = sourcefile.split('.').collect(); let q: Vec<&str> = sourcefile.split('.').collect();
let bin_filename = match &q[..] { let bin_filename = match &q[..] {
&[name, "schala"] => name, &[name, "maaru"] => name,
_ => panic!("Bad filename {}", sourcefile), _ => panic!("Bad filename {}", sourcefile),
}; };

169
src/robo_lang/mod.rs Normal file
View File

@ -0,0 +1,169 @@
extern crate itertools;
use self::itertools::Itertools;
use language::{ProgrammingLanguage, EvaluationMachine, ParseError, TokenError, LLVMCodeString};
pub struct Robo {
}
impl Robo {
pub fn new() -> Robo {
Robo { }
}
}
pub struct RoboEvaluator {
pub trace_evaluation: bool,
}
#[derive(Debug)]
pub enum Token {
StrLiteral(String),
Backtick,
Newline,
LParen,
RParen,
LBracket,
RBracket,
LBrace,
RBrace,
Period,
Comma,
Colon,
Semicolon,
SingleQuote,
Identifier(String),
Operator(String),
NumLiteral(Number),
}
#[derive(Debug)]
pub enum Number {
IntegerRep(String),
FloatRep(String)
}
pub type AST = Vec<ASTNode>;
#[derive(Debug)]
pub enum ASTNode {
FunctionDefinition(String, Expression),
ImportStatement(String),
}
#[derive(Debug)]
pub enum Expression {
}
impl ProgrammingLanguage for Robo {
type Token = Token;
type AST = AST;
type Evaluator = RoboEvaluator;
fn name() -> String {
"Robo".to_string()
}
fn tokenize(input: &str) -> Result<Vec<Self::Token>, TokenError> {
use self::Token::*;
let mut tokens = Vec::new();
let mut iter = input.chars().peekable();
while let Some(c) = iter.next() {
if c == ';' {
while let Some(c) = iter.next() {
if c == '\n' {
break;
}
}
continue;
}
let cur_tok = match c {
c if char::is_whitespace(c) && c != '\n' => continue,
'\n' => Newline,
'(' => LParen,
')' => RParen,
'[' => LBracket,
']' => RBracket,
'{' => LBrace,
'}' => RBrace,
',' => Comma,
':' => Colon,
';' => Semicolon,
'.' => Period,
'`' => Backtick,
'\'' => SingleQuote,
'"' => {
let mut buffer = String::new();
loop {
match iter.next() {
Some(x) if x == '"' => break,
Some(x) => buffer.push(x),
None => return Err(TokenError::new("Unclosed quote")),
}
}
StrLiteral(buffer)
}
c if c.is_digit(10) => {
let mut integer = true;
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
if let Some(&'.') = iter.peek() {
buffer.push(iter.next().unwrap());
integer = false;
}
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
let inner = if integer {
Number::IntegerRep(buffer)
} else {
Number::FloatRep(buffer)
};
NumLiteral(inner)
},
c if char::is_alphanumeric(c) => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| char::is_alphanumeric(*x)));
Identifier(buffer)
},
c => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| !char::is_whitespace(*x)));
Operator(buffer)
}
};
tokens.push(cur_tok);
}
Ok(tokens)
}
fn parse(_input: Vec<Self::Token>) -> Result<Self::AST, ParseError> {
Ok(vec!())
}
fn evaluate(_ast: Self::AST, _evaluator: &mut Self::Evaluator) -> Vec<String> {
vec!["Unimplemented".to_string()]
}
fn compile(_ast: Self::AST) -> LLVMCodeString {
unimplemented!()
}
}
impl EvaluationMachine for RoboEvaluator {
fn set_option(&mut self, option: &str, value: bool) -> bool {
if option == "trace_evaluation" {
self.trace_evaluation = value;
return true;
}
false
}
fn new() -> RoboEvaluator {
RoboEvaluator {
trace_evaluation: false,
}
}
}

View File

@ -1,54 +0,0 @@
use ::std::marker::PhantomData;
pub mod tokenizer;
pub mod parser;
pub mod eval;
pub mod compilation;
use language::{ProgrammingLanguage, EvaluationMachine, ParseError, TokenError, LLVMCodeString};
pub use self::eval::Evaluator as SchalaEvaluator;
pub struct Schala<'a> { marker: PhantomData<&'a ()> }
impl<'a> Schala<'a> {
pub fn new() -> Schala<'a> {
Schala { marker: PhantomData }
}
}
impl<'a> ProgrammingLanguage for Schala<'a> {
type Token = tokenizer::Token;
type AST = parser::AST;
type Evaluator = SchalaEvaluator<'a>;
fn name() -> String {
"Schala".to_string()
}
fn tokenize(input: &str) -> Result<Vec<Self::Token>, TokenError> {
tokenizer::tokenize(input)
}
fn parse(input: Vec<Self::Token>) -> Result<Self::AST, ParseError> {
parser::parse(&input, &[]).map_err(|x| ParseError { msg: x.msg })
}
fn evaluate(ast: Self::AST, evaluator: &mut Self::Evaluator) -> Vec<String> {
evaluator.run(ast)
}
fn compile(ast: Self::AST) -> LLVMCodeString {
compilation::compile_ast(ast)
}
}
impl<'a> EvaluationMachine for SchalaEvaluator<'a> {
fn set_option(&mut self, option: &str, value: bool) -> bool {
if option == "trace_evaluation" {
self.trace_evaluation = value;
return true;
}
false
}
fn new() -> SchalaEvaluator<'a> {
SchalaEvaluator::new(None)
}
}