Grand renaming of things
This commit is contained in:
parent
801896bcc6
commit
dd22ca0291
57
README.md
57
README.md
@ -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
|
||||
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.
|
||||
|
||||
Schala started out life as an experiment in writing a Javascript-like
|
||||
programming language that would never encounter any kind of runtime
|
||||
value error, but rather always return `null` under any kind of error
|
||||
condition. I had seen one too many Javascript `Uncaught TypeError:
|
||||
Cannot read property ___ of undefined` messages, and I was a bit frustrated.
|
||||
Plus I had always wanted to write a programming langauge from scratch,
|
||||
and Rust is a fun language to program in.
|
||||
programming language that would never encounter any kind of runtime value
|
||||
error, but rather always return `null` under any kind of error condition. I had
|
||||
seen one too many Javascript `Uncaught TypeError: Cannot read property ___ of
|
||||
undefined` messages, and I was a bit frustrated. Plus I had always wanted to
|
||||
write a programming langauge from scratch, and Rust is a fun language to
|
||||
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
|
||||
of programming languages as well, and wanted to make the process
|
||||
as general as possible. I changed the name of the project to
|
||||
Schala, after the Princess of Zeal from *Chrono Trigger*, because I
|
||||
like classic JRPGs and because it sounds sort of like Scala.
|
||||
The name of the project comes from Schala the Princess of Zeal from the 1995
|
||||
SNES RPG *Chrono Trigger*. I like classic JRPGs and enjoyed the thought of
|
||||
creating a language name confusingly close to Scala. The naming scheme for
|
||||
languages implemented with the Schala meta-interpreter is Chrono Trigger
|
||||
characters.
|
||||
|
||||
Schala is as yet an incomplete personal project that I continue to work
|
||||
on as my time permits.
|
||||
## Languages implemented using the meta-interpreter
|
||||
|
||||
### 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
|
||||
of learning how to write a programming language.
|
||||
|
||||
#### Evaluation
|
||||
### Evaluation
|
||||
*Understanding Computation*, Tom Stuart, O'Reilly 2013
|
||||
|
||||
#### Parsing
|
||||
### Parsing
|
||||
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
|
||||
[Crafting Interpreters](http://www.craftinginterpreters.com/)
|
||||
|
||||
#### LLVM
|
||||
### LLVM
|
||||
http://blog.ulysse.io/2016/07/03/llvm-getting-started.html
|
||||
|
@ -5,7 +5,7 @@ use std::collections::HashMap;
|
||||
use self::llvm_sys::prelude::*;
|
||||
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 llvm_wrap as LLVMWrap;
|
@ -2,13 +2,13 @@ extern crate take_mut;
|
||||
|
||||
use std::collections::HashMap;
|
||||
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::io::{Write, Stdout, BufWriter};
|
||||
use std::convert::From;
|
||||
|
||||
use schala_lang::parser::Expression::*;
|
||||
use schala_lang::parser::Statement::*;
|
||||
use maaru_lang::parser::Expression::*;
|
||||
use maaru_lang::parser::Statement::*;
|
||||
|
||||
type Reduction<T> = (T, Option<SideEffect>);
|
||||
|
@ -1,157 +1,44 @@
|
||||
extern crate itertools;
|
||||
use self::itertools::Itertools;
|
||||
use ::std::marker::PhantomData;
|
||||
pub mod tokenizer;
|
||||
pub mod parser;
|
||||
pub mod eval;
|
||||
pub mod compilation;
|
||||
|
||||
use language::{ProgrammingLanguage, EvaluationMachine, ParseError, TokenError, LLVMCodeString};
|
||||
|
||||
pub struct Maaru {
|
||||
}
|
||||
pub use self::eval::Evaluator as MaaruEvaluator;
|
||||
|
||||
impl Maaru {
|
||||
pub fn new() -> Maaru {
|
||||
Maaru { }
|
||||
pub struct Maaru<'a> { marker: PhantomData<&'a ()> }
|
||||
impl<'a> Maaru<'a> {
|
||||
pub fn new() -> Maaru <'a> {
|
||||
Maaru { marker: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MaaruEvaluator {
|
||||
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 Maaru {
|
||||
type Token = Token;
|
||||
type AST = AST;
|
||||
type Evaluator = MaaruEvaluator;
|
||||
impl<'a> ProgrammingLanguage for Maaru<'a> {
|
||||
type Token = tokenizer::Token;
|
||||
type AST = parser::AST;
|
||||
type Evaluator = MaaruEvaluator<'a>;
|
||||
|
||||
fn name() -> String {
|
||||
"Maaru".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)
|
||||
tokenizer::tokenize(input)
|
||||
}
|
||||
|
||||
fn parse(_input: Vec<Self::Token>) -> Result<Self::AST, ParseError> {
|
||||
Ok(vec!())
|
||||
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> {
|
||||
vec!["Unimplemented".to_string()]
|
||||
fn evaluate(ast: Self::AST, evaluator: &mut Self::Evaluator) -> Vec<String> {
|
||||
evaluator.run(ast)
|
||||
}
|
||||
fn compile(_ast: Self::AST) -> LLVMCodeString {
|
||||
unimplemented!()
|
||||
fn compile(ast: Self::AST) -> LLVMCodeString {
|
||||
compilation::compile_ast(ast)
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluationMachine for MaaruEvaluator {
|
||||
impl<'a> EvaluationMachine for MaaruEvaluator<'a> {
|
||||
fn set_option(&mut self, option: &str, value: bool) -> bool {
|
||||
if option == "trace_evaluation" {
|
||||
self.trace_evaluation = value;
|
||||
@ -161,9 +48,7 @@ impl EvaluationMachine for MaaruEvaluator {
|
||||
false
|
||||
}
|
||||
|
||||
fn new() -> MaaruEvaluator {
|
||||
MaaruEvaluator {
|
||||
trace_evaluation: false,
|
||||
}
|
||||
fn new() -> MaaruEvaluator<'a> {
|
||||
MaaruEvaluator::new(None)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use schala_lang::tokenizer::{Token, Kw, OpTok};
|
||||
use schala_lang::tokenizer::Token::*;
|
||||
use maaru_lang::tokenizer::{Token, Kw, OpTok};
|
||||
use maaru_lang::tokenizer::Token::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::collections::VecDeque;
|
11
src/main.rs
11
src/main.rs
@ -9,11 +9,14 @@ use std::process;
|
||||
use std::io::Write;
|
||||
use std::default::Default;
|
||||
|
||||
/*
|
||||
mod schala_lang;
|
||||
use schala_lang::SchalaEvaluator;
|
||||
use schala_lang::Schala;
|
||||
*/
|
||||
|
||||
mod maaru_lang;
|
||||
mod robo_lang;
|
||||
|
||||
mod language;
|
||||
use language::{ProgrammingLanguage, LanguageInterface, LLVMCodeString, EvaluationMachine};
|
||||
@ -26,8 +29,8 @@ use virtual_machine::{run_vm, run_assembler};
|
||||
fn main() {
|
||||
let languages: Vec<Box<LanguageInterface>> =
|
||||
vec![
|
||||
Box::new((Schala::new(), SchalaEvaluator::new(None))),
|
||||
Box::new((maaru_lang::Maaru::new(), maaru_lang::MaaruEvaluator::new())),
|
||||
Box::new((maaru_lang::Maaru::new(), maaru_lang::MaaruEvaluator::new(None))),
|
||||
Box::new((robo_lang::Robo::new(), robo_lang::RoboEvaluator::new())),
|
||||
];
|
||||
|
||||
let option_matches =
|
||||
@ -79,7 +82,7 @@ fn main() {
|
||||
repl.run();
|
||||
}
|
||||
[_, ref filename, _..] => {
|
||||
let language = Schala::new();
|
||||
let language = maaru_lang::Maaru::new();
|
||||
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 q: Vec<&str> = sourcefile.split('.').collect();
|
||||
let bin_filename = match &q[..] {
|
||||
&[name, "schala"] => name,
|
||||
&[name, "maaru"] => name,
|
||||
_ => panic!("Bad filename {}", sourcefile),
|
||||
};
|
||||
|
||||
|
169
src/robo_lang/mod.rs
Normal file
169
src/robo_lang/mod.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user