forked from AbleScript/ablescript
Merge pull request #9 from erindesu/master
This commit is contained in:
commit
19762e940a
70
Cargo.lock
generated
70
Cargo.lock
generated
|
@ -8,6 +8,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"logos",
|
"logos",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -42,6 +43,12 @@ version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.33.3"
|
||||||
|
@ -63,6 +70,17 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.18"
|
version = "0.1.18"
|
||||||
|
@ -102,6 +120,12 @@ dependencies = [
|
||||||
"utf8-ranges",
|
"utf8-ranges",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
|
@ -120,6 +144,46 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.23"
|
version = "0.6.23"
|
||||||
|
@ -176,6 +240,12 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -9,3 +9,4 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap="*"
|
clap="*"
|
||||||
logos = "0.12"
|
logos = "0.12"
|
||||||
|
rand = "*"
|
10
able-script-test/parse_test.able
Normal file
10
able-script-test/parse_test.able
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
functio test() {
|
||||||
|
functio nested() {
|
||||||
|
var c = false;
|
||||||
|
}
|
||||||
|
var a = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
functio another() {
|
||||||
|
var b = false;
|
||||||
|
}
|
19
src/error.rs
Normal file
19
src/error.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Error {
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub position: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
SyntaxError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn panic(&self, span: &str) {
|
||||||
|
println!("{:?} occured at {:?}", self.kind, self.position);
|
||||||
|
println!(" {}", &span);
|
||||||
|
}
|
||||||
|
}
|
16
src/main.rs
16
src/main.rs
|
@ -1,14 +1,15 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
mod base_55;
|
mod base_55;
|
||||||
|
mod error;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod scanner;
|
|
||||||
mod tokens;
|
mod tokens;
|
||||||
mod variables;
|
mod variables;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use scanner::Scanner;
|
use parser::Parser;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
variables::test();
|
// variables::test();
|
||||||
|
|
||||||
let matches = App::new("AbleScript")
|
let matches = App::new("AbleScript")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
@ -29,9 +30,10 @@ fn main() {
|
||||||
// Read file
|
// Read file
|
||||||
let source = std::fs::read_to_string(file_path).unwrap();
|
let source = std::fs::read_to_string(file_path).unwrap();
|
||||||
|
|
||||||
// Print token type: `value`
|
// Parse
|
||||||
let mut scanner = Scanner::new(&source);
|
let mut parser = Parser::new(&source);
|
||||||
scanner.scan();
|
let ast = parser.parse();
|
||||||
|
println!("{:#?}", ast);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("hi");
|
println!("hi");
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::tokens::Abool;
|
|
||||||
|
|
||||||
pub fn abool2num(abool: Abool) -> i32 {
|
|
||||||
match abool {
|
|
||||||
Abool::Never => -1,
|
|
||||||
Abool::Sometimes => 0,
|
|
||||||
Abool::Always => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn num2abool(number: i32) -> Abool {
|
|
||||||
match number {
|
|
||||||
-1 => Abool::Never,
|
|
||||||
0 => Abool::Sometimes,
|
|
||||||
1 => Abool::Always,
|
|
||||||
_ => Abool::Sometimes,
|
|
||||||
}
|
|
||||||
}
|
|
6
src/parser/item.rs
Normal file
6
src/parser/item.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Expr {
|
||||||
|
VariableDeclaration { iden: String, init: Option<String> },
|
||||||
|
FunctionDeclaration { iden: String, body: Vec<Expr> },
|
||||||
|
BfFDeclaration { iden: String, code: String },
|
||||||
|
}
|
98
src/parser/mod.rs
Normal file
98
src/parser/mod.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
mod item;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use item::Expr;
|
||||||
|
|
||||||
|
use crate::error::{Error, ErrorKind};
|
||||||
|
use crate::tokens::Token;
|
||||||
|
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
/// Parser structure / state machine
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
lexer: logos::Lexer<'a, Token>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Create a new parser object
|
||||||
|
pub fn new(source: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
lexer: Token::lexer(source),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start parsing Token Vector into Abstract Syntax Tree
|
||||||
|
pub fn parse(&mut self) -> Vec<Expr> {
|
||||||
|
let mut ast = vec![];
|
||||||
|
while let Some(token) = self.lexer.next() {
|
||||||
|
let expr = match token {
|
||||||
|
Token::Variable => self.variable_declaration(),
|
||||||
|
Token::Function => self.function_declaration(),
|
||||||
|
Token::BfFunction => self.bff_declaration(),
|
||||||
|
Token::RightBrace => return ast,
|
||||||
|
_ => Err(Error {
|
||||||
|
kind: ErrorKind::SyntaxError,
|
||||||
|
position: 0..0,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
match expr {
|
||||||
|
Ok(o) => ast.push(o),
|
||||||
|
Err(e) => {
|
||||||
|
e.panic(self.lexer.slice());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse variable declaration
|
||||||
|
///
|
||||||
|
/// `var [iden] = [literal];`
|
||||||
|
fn variable_declaration(&mut self) -> Result<Expr, Error> {
|
||||||
|
let iden = self.require(Token::Identifier)?;
|
||||||
|
|
||||||
|
let init = match self.lexer.next() {
|
||||||
|
Some(Token::Semicolon) => None,
|
||||||
|
Some(Token::Assignment) => {
|
||||||
|
let value = self.require(Token::Boolean)?; // TODO: Shouldn't be limited to boolean (pattern match?)
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::SyntaxError,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Expr::VariableDeclaration { iden, init })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declare function
|
||||||
|
///
|
||||||
|
/// `functio [iden] ([expr], [expr]) { ... }
|
||||||
|
fn function_declaration(&mut self) -> Result<Expr, Error> {
|
||||||
|
let iden = self.require(Token::Identifier)?;
|
||||||
|
self.require(Token::LeftParenthesis)?;
|
||||||
|
// TODO: Arguments
|
||||||
|
self.require(Token::RightParenthesis)?;
|
||||||
|
self.require(Token::LeftBrace)?;
|
||||||
|
let body = self.parse();
|
||||||
|
|
||||||
|
Ok(Expr::FunctionDeclaration { iden, body })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declare BF FFI Function
|
||||||
|
///
|
||||||
|
/// `bff [iden] { ... }`
|
||||||
|
fn bff_declaration(&mut self) -> Result<Expr, Error> {
|
||||||
|
let iden = self.require(Token::Identifier)?;
|
||||||
|
self.require(Token::LeftBrace)?;
|
||||||
|
let code = self.require(Token::String)?; // <-- Nasty hack, but works
|
||||||
|
self.require(Token::RightBrace)?;
|
||||||
|
Ok(Expr::BfFDeclaration { iden, code })
|
||||||
|
}
|
||||||
|
}
|
35
src/parser/utils.rs
Normal file
35
src/parser/utils.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::error::{Error, ErrorKind};
|
||||||
|
use crate::tokens::Token;
|
||||||
|
use crate::variables::Abool;
|
||||||
|
|
||||||
|
use super::Parser;
|
||||||
|
|
||||||
|
pub fn abool2num(abool: Abool) -> i32 {
|
||||||
|
match abool {
|
||||||
|
Abool::Never => -1,
|
||||||
|
Abool::Sometimes => 0,
|
||||||
|
Abool::Always => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn num2abool(number: i32) -> Abool {
|
||||||
|
match number {
|
||||||
|
-1 => Abool::Never,
|
||||||
|
0 => Abool::Sometimes,
|
||||||
|
1 => Abool::Always,
|
||||||
|
_ => Abool::Sometimes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Require type of token as next and return it's value (sometimes irrelevant)
|
||||||
|
pub(super) fn require(&mut self, with: Token) -> Result<String, Error> {
|
||||||
|
if self.lexer.next() == Some(with) {
|
||||||
|
Ok(self.lexer.slice().to_owned())
|
||||||
|
} else {
|
||||||
|
Err(Error {
|
||||||
|
kind: ErrorKind::SyntaxError,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +0,0 @@
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use logos::Logos;
|
|
||||||
|
|
||||||
use crate::tokens::{self, Token};
|
|
||||||
pub struct Scanner<'a> {
|
|
||||||
source: &'a str,
|
|
||||||
lexer: logos::Lexer<'a, Token>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Scanner<'a> {
|
|
||||||
pub fn new(source: &'a str) -> Self {
|
|
||||||
Self {
|
|
||||||
source,
|
|
||||||
lexer: tokens::Token::lexer(source),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scan(&mut self) {
|
|
||||||
while let Some(tok) = self.lexer.next() {
|
|
||||||
if matches!(tok, Token::Error) {
|
|
||||||
self.throw_err(&self.lexer.span());
|
|
||||||
} else {
|
|
||||||
println!("Token: {:?}", tok);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn throw_err(&self, location: &Range<usize>) {
|
|
||||||
let part = &self.source[location.clone()];
|
|
||||||
println!("Unknown keyword `{}` found on {:?}", part, location);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,23 @@ use logos::Logos;
|
||||||
|
|
||||||
#[derive(Logos, Debug, PartialEq)]
|
#[derive(Logos, Debug, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
|
// Literals
|
||||||
|
/// True, False
|
||||||
|
#[regex("true|false")]
|
||||||
|
Boolean,
|
||||||
|
|
||||||
|
/// Always, Sometimes, Never
|
||||||
|
#[regex("always|sometimes|never")]
|
||||||
|
Aboolean,
|
||||||
|
|
||||||
|
/// String
|
||||||
|
#[regex("\"(\\.|[^\"])*\"")]
|
||||||
|
String,
|
||||||
|
|
||||||
|
/// Integer
|
||||||
|
#[regex(r"[0-9]+")]
|
||||||
|
Integer,
|
||||||
|
|
||||||
/// A C-complaint identifier
|
/// A C-complaint identifier
|
||||||
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*")]
|
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*")]
|
||||||
Identifier,
|
Identifier,
|
||||||
|
@ -30,6 +47,7 @@ pub enum Token {
|
||||||
#[regex(r"#.*")]
|
#[regex(r"#.*")]
|
||||||
Comment,
|
Comment,
|
||||||
|
|
||||||
|
// Operators
|
||||||
#[token("-")]
|
#[token("-")]
|
||||||
Subtract,
|
Subtract,
|
||||||
|
|
||||||
|
@ -60,22 +78,6 @@ pub enum Token {
|
||||||
#[token("var")]
|
#[token("var")]
|
||||||
Variable,
|
Variable,
|
||||||
|
|
||||||
/// True, False
|
|
||||||
#[regex("true|false")]
|
|
||||||
Boolean,
|
|
||||||
|
|
||||||
/// Always, Sometimes, Never
|
|
||||||
#[regex("always|sometimes|never")]
|
|
||||||
Aboolean,
|
|
||||||
|
|
||||||
/// String
|
|
||||||
#[regex("\"(\\.|[^\"])*\"")]
|
|
||||||
String,
|
|
||||||
|
|
||||||
/// Integer
|
|
||||||
#[regex(r"[0-9]+")]
|
|
||||||
Integer,
|
|
||||||
|
|
||||||
/// Prints the preceding things
|
/// Prints the preceding things
|
||||||
#[token("print")]
|
#[token("print")]
|
||||||
Print,
|
Print,
|
||||||
|
@ -87,14 +89,17 @@ pub enum Token {
|
||||||
#[token("T-Dark")]
|
#[token("T-Dark")]
|
||||||
TDark,
|
TDark,
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
#[token("if")]
|
||||||
|
If,
|
||||||
|
|
||||||
|
#[token("else")]
|
||||||
|
Else,
|
||||||
|
|
||||||
|
#[token("loop")]
|
||||||
|
Loop,
|
||||||
|
|
||||||
#[regex(r"[ \t\n\f]+", logos::skip)]
|
#[regex(r"[ \t\n\f]+", logos::skip)]
|
||||||
#[error]
|
#[error]
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Abool {
|
|
||||||
Never = -1,
|
|
||||||
Sometimes = 0,
|
|
||||||
Always = 1,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,29 @@
|
||||||
|
use rand::Rng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum Value {
|
pub enum Abool {
|
||||||
|
Never = -1,
|
||||||
|
Sometimes = 0,
|
||||||
|
Always = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<bool> for Abool {
|
||||||
|
fn into(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Abool::Never => false,
|
||||||
|
Abool::Always => true,
|
||||||
|
Abool::Sometimes => rand::thread_rng().gen(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Value {
|
||||||
Str(String),
|
Str(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
//TODO(Able): Add abool and other variable types
|
Abool(Abool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in a new issue