mirror of
https://github.com/mii443/gpsl.git
synced 2025-08-22 15:55:26 +00:00
init
This commit is contained in:
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
// IntelliSense を使用して利用可能な属性を学べます。
|
||||
// 既存の属性の説明をホバーして表示します。
|
||||
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/<your program>",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
55
Cargo.lock
generated
Normal file
55
Cargo.lock
generated
Normal file
@ -0,0 +1,55 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpsl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "gpsl"
|
||||
version = "0.1.0"
|
||||
authors = ["mii"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
uuid = { version = "0.8.1", features = ["serde", "v4"] }
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 mii8080
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
415
src/gpsl.rs
Normal file
415
src/gpsl.rs
Normal file
@ -0,0 +1,415 @@
|
||||
use crate::node::*;
|
||||
use crate::parser::*;
|
||||
use crate::source::Source;
|
||||
use crate::tokenizer::*;
|
||||
use crate::variable::*;
|
||||
use std::collections::HashMap;
|
||||
use std::string::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct GPSL {
|
||||
pub source: Source,
|
||||
pub l_vars: HashMap<String, LocalVariable>,
|
||||
pub assembly: String,
|
||||
pub offset_size: usize,
|
||||
}
|
||||
|
||||
pub struct LocalVariable {
|
||||
pub name: String,
|
||||
pub value: Variable,
|
||||
pub status: VariableStatus,
|
||||
}
|
||||
|
||||
pub struct VariableStatus {
|
||||
pub initialized: bool,
|
||||
}
|
||||
|
||||
impl VariableStatus {
|
||||
pub fn default() -> VariableStatus {
|
||||
VariableStatus { initialized: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl GPSL {
|
||||
pub fn new(source: Source) -> GPSL {
|
||||
GPSL {
|
||||
source,
|
||||
l_vars: HashMap::new(),
|
||||
assembly: String::from(""),
|
||||
offset_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_number(node: Variable) -> Result<usize, String> {
|
||||
match node {
|
||||
Variable::Number { value } => {
|
||||
Ok(value)
|
||||
},
|
||||
_ => {
|
||||
Err(String::from("Not a number"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, node: Box<Node>) -> Result<Option<Variable>, String> {
|
||||
match *node {
|
||||
Node::Text { value } => {
|
||||
Ok(Some(Variable::Text {
|
||||
value
|
||||
}))
|
||||
}
|
||||
Node::Number { value } => {
|
||||
Ok(Some(Variable::Number {
|
||||
value
|
||||
}))
|
||||
}
|
||||
Node::Operator { kind, lhs, rhs } => {
|
||||
if kind == NodeKind::ASSIGN {
|
||||
let rhs = self.evaluate(rhs);
|
||||
|
||||
if let Ok(Some(rhs)) = rhs {
|
||||
match *(lhs.clone()) {
|
||||
Node::Lvar { value } => {
|
||||
self.l_vars.get_mut(&value).unwrap().value = rhs;
|
||||
self.l_vars.get_mut(&value).unwrap().status.initialized = true
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
let lhs = self.evaluate(lhs).expect("Cannot evaluate lhs.");
|
||||
let rhs = self.evaluate(rhs).expect("Cannot evaluate rhs.");
|
||||
|
||||
if let Some(lhs) = lhs {
|
||||
if let Some(rhs) = rhs {
|
||||
match kind {
|
||||
NodeKind::ADD => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
Ok(Some(Variable::Number {
|
||||
value: lhs + rhs
|
||||
}))
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
NodeKind::DIV => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
Ok(Some(Variable::Number {
|
||||
value: lhs / rhs
|
||||
}))
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
NodeKind::MUL => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
Ok(Some(Variable::Number {
|
||||
value: lhs * rhs
|
||||
}))
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
NodeKind::SUB => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
Ok(Some(Variable::Number {
|
||||
value: lhs - rhs
|
||||
}))
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
|
||||
NodeKind::EQ => {
|
||||
if lhs == rhs {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 1
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 0
|
||||
}))
|
||||
}
|
||||
},
|
||||
NodeKind::NE => {
|
||||
if lhs != rhs {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 1
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 0
|
||||
}))
|
||||
}
|
||||
},
|
||||
NodeKind::LT => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
if lhs < rhs {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 1
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 0
|
||||
}))
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
NodeKind::LE => {
|
||||
match GPSL::extract_number(lhs) {
|
||||
Ok(lhs) => {
|
||||
match GPSL::extract_number(rhs) {
|
||||
Ok(rhs) => {
|
||||
if lhs <= rhs {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 1
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(Variable::Number {
|
||||
value: 0
|
||||
}))
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
}
|
||||
Err(err) => { Err(err) }
|
||||
}
|
||||
},
|
||||
_ => Ok(None)
|
||||
}
|
||||
} else {
|
||||
Err(String::from("RHS Variable is null."))
|
||||
}
|
||||
} else {
|
||||
Err(String::from("LHS Variable is null."))
|
||||
}
|
||||
}
|
||||
Node::Lvar { value } => {
|
||||
return Ok(Some(self.l_vars.get(&value).unwrap().value.clone()));
|
||||
}
|
||||
Node::Return { lhs } => {
|
||||
if let Ok(Some(lhs)) = self.evaluate(lhs) {
|
||||
return Ok(Some(Variable::Return {
|
||||
value: Box::new(lhs)
|
||||
}));
|
||||
} else {
|
||||
return Err(String::from("Cannot evaluate LHS."));
|
||||
}
|
||||
}
|
||||
Node::If {
|
||||
condition,
|
||||
stmt,
|
||||
else_stmt,
|
||||
} => {
|
||||
if let Ok(Some(condition)) = self.evaluate(condition) {
|
||||
if match condition {
|
||||
Variable::Number { value } => value == 1,
|
||||
_ => false
|
||||
} {
|
||||
if let Ok(Some(res)) = self.evaluate(stmt) {
|
||||
match res.clone() {
|
||||
Variable::Return { value } => {
|
||||
return Ok(Some(res));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match else_stmt {
|
||||
Some(else_stmt) => {
|
||||
if let Ok(Some(res)) = self.evaluate(else_stmt) {
|
||||
match res.clone() {
|
||||
Variable::Return { value } => {
|
||||
return Ok(Some(res));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
Node::While { condition, stmt } => {
|
||||
let mut cond = if let Some(condition) = self.evaluate(condition.clone())? {
|
||||
condition
|
||||
} else {
|
||||
Variable::Number {
|
||||
value: 0
|
||||
}
|
||||
};
|
||||
|
||||
while match cond {
|
||||
Variable::Number { value } => value == 1,
|
||||
_ => false
|
||||
} {
|
||||
self.evaluate(stmt.clone())?;
|
||||
cond = if let Some(condition) = self.evaluate(condition.clone())? {
|
||||
condition
|
||||
} else {
|
||||
Variable::Number {
|
||||
value: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
Node::For {
|
||||
init,
|
||||
condition,
|
||||
update,
|
||||
stmt,
|
||||
} => {
|
||||
match init {
|
||||
Some(init) => {self.evaluate(init)?;},
|
||||
None => {}
|
||||
}
|
||||
|
||||
let mut cond = match condition.clone() {
|
||||
Some(condition) => {
|
||||
if let Some(condition) = self.evaluate(condition)? {
|
||||
condition
|
||||
} else {
|
||||
Variable::Number {
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Variable::Number {
|
||||
value: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while match cond {
|
||||
Variable::Number { value } => value == 1,
|
||||
_ => false
|
||||
} {
|
||||
self.evaluate(stmt.clone())?;
|
||||
|
||||
match update.clone() {
|
||||
Some(update) => {self.evaluate(update)?;},
|
||||
None => {}
|
||||
}
|
||||
|
||||
cond = match condition.clone() {
|
||||
Some(condition) => {
|
||||
if let Some(condition) = self.evaluate(condition)? {
|
||||
condition
|
||||
} else {
|
||||
Variable::Number {
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Variable::Number {
|
||||
value: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
Node::Block { stmts } => {
|
||||
for stmt in stmts {
|
||||
let ret = self.evaluate(stmt)?;
|
||||
if let Some(ret) = ret {
|
||||
match ret.clone() {
|
||||
Variable::Return { value } => {
|
||||
return Ok(Some(ret));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
Node::Define { name, var_type } => {
|
||||
let value = if var_type == "num" {
|
||||
Variable::Number {
|
||||
value: 0
|
||||
}
|
||||
} else {
|
||||
return Err(format!("{}: 未知の型です。", var_type));
|
||||
};
|
||||
self.l_vars.insert(
|
||||
name.clone(),
|
||||
LocalVariable {
|
||||
name,
|
||||
value,
|
||||
status: VariableStatus::default(),
|
||||
},
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<Variable, String> {
|
||||
let mut tokenizer = Tokenizer::new();
|
||||
|
||||
tokenizer.tokenize(&mut self.source)?;
|
||||
|
||||
let mut parser = Parser {
|
||||
tokenizer: tokenizer.clone(),
|
||||
local_vars: HashMap::new(),
|
||||
};
|
||||
|
||||
let programs = parser.program()?;
|
||||
|
||||
for program in programs {
|
||||
if let Ok(Some(res)) = self.evaluate(program) {
|
||||
match res {
|
||||
Variable::Return { value } => {
|
||||
return Ok(*value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Variable::None {})
|
||||
}
|
||||
}
|
7
src/lib.rs
Normal file
7
src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub mod node;
|
||||
pub mod parser;
|
||||
pub mod gpsl;
|
||||
pub mod source;
|
||||
pub mod token;
|
||||
pub mod tokenizer;
|
||||
pub mod variable;
|
69
src/node.rs
Normal file
69
src/node.rs
Normal file
@ -0,0 +1,69 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum NodeKind {
|
||||
ASSIGN,
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
EQ, // ==
|
||||
NE, // !=
|
||||
LT, // <
|
||||
LE, // <=
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Node {
|
||||
Operator {
|
||||
kind: NodeKind,
|
||||
lhs: Box<Node>,
|
||||
rhs: Box<Node>,
|
||||
},
|
||||
Number {
|
||||
value: usize,
|
||||
},
|
||||
Text {
|
||||
value: String,
|
||||
},
|
||||
Lvar {
|
||||
value: String,
|
||||
},
|
||||
Return {
|
||||
lhs: Box<Node>,
|
||||
},
|
||||
If {
|
||||
condition: Box<Node>,
|
||||
stmt: Box<Node>,
|
||||
else_stmt: Option<Box<Node>>,
|
||||
},
|
||||
While {
|
||||
condition: Box<Node>,
|
||||
stmt: Box<Node>,
|
||||
},
|
||||
For {
|
||||
init: Option<Box<Node>>,
|
||||
condition: Option<Box<Node>>,
|
||||
update: Option<Box<Node>>,
|
||||
stmt: Box<Node>,
|
||||
},
|
||||
Block {
|
||||
stmts: Vec<Box<Node>>,
|
||||
},
|
||||
Define {
|
||||
name: String,
|
||||
var_type: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new_node(kind: NodeKind, lhs: Box<Node>, rhs: Box<Node>) -> Box<Node> {
|
||||
Box::new(Node::Operator { kind, lhs, rhs })
|
||||
}
|
||||
|
||||
pub fn new_num_node(value: usize) -> Box<Node> {
|
||||
Box::new(Node::Number { value })
|
||||
}
|
||||
|
||||
pub fn new_lvar_node(value: String) -> Box<Node> {
|
||||
Box::new(Node::Lvar { value })
|
||||
}
|
||||
}
|
263
src/parser.rs
Normal file
263
src/parser.rs
Normal file
@ -0,0 +1,263 @@
|
||||
use crate::node::*;
|
||||
use crate::token::*;
|
||||
use crate::tokenizer::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parser {
|
||||
pub tokenizer: Tokenizer,
|
||||
pub local_vars: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn program(&mut self) -> Result<Vec<Box<Node>>, String> {
|
||||
let mut nodes: Vec<Box<Node>> = vec![];
|
||||
loop {
|
||||
if self.tokenizer.current_token().kind != TokenKind::EOF {
|
||||
nodes.push(self.stmt()?);
|
||||
} else {
|
||||
return Ok(nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stmt(&mut self) -> Result<Box<Node>, String> {
|
||||
if self
|
||||
.tokenizer
|
||||
.consume_kind_str(TokenKind::IDENT, String::from("let"))
|
||||
{
|
||||
let ident = self.tokenizer.current_token().clone();
|
||||
self.tokenizer.expect_kind(TokenKind::IDENT)?;
|
||||
self.tokenizer.expect(String::from(":"))?;
|
||||
let var_type = self.tokenizer.current_token().clone();
|
||||
self.tokenizer.expect_kind(TokenKind::IDENT)?;
|
||||
self.tokenizer.expect(String::from(";"))?;
|
||||
return Ok(Box::new(Node::Define {
|
||||
name: ident.str,
|
||||
var_type: var_type.str,
|
||||
}));
|
||||
}
|
||||
|
||||
if self
|
||||
.tokenizer
|
||||
.consume_kind_str(TokenKind::RESERVED, String::from("{"))
|
||||
{
|
||||
let mut stmts: Vec<Box<Node>> = vec![];
|
||||
loop {
|
||||
if self
|
||||
.tokenizer
|
||||
.consume_kind_str(TokenKind::RESERVED, String::from("}"))
|
||||
{
|
||||
return Ok(Box::new(Node::Block { stmts }));
|
||||
} else {
|
||||
stmts.push(self.stmt()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.tokenizer.consume_kind(TokenKind::RETURN) {
|
||||
let node = Node::Return { lhs: self.expr()? };
|
||||
self.tokenizer.expect(String::from(";"))?;
|
||||
return Ok(Box::new(node));
|
||||
}
|
||||
|
||||
if self.tokenizer.current_token().kind == TokenKind::CONTROL {
|
||||
match &*self.tokenizer.current_token().str {
|
||||
"if" => {
|
||||
self.tokenizer.cursor += 1;
|
||||
self.tokenizer.expect(String::from("("))?;
|
||||
let condition = self.expr()?;
|
||||
self.tokenizer.expect(String::from(")"))?;
|
||||
let stmt = self.stmt()?;
|
||||
let mut else_stmt: Option<Box<Node>> = None;
|
||||
if self
|
||||
.tokenizer
|
||||
.consume_kind_str(TokenKind::CONTROL, String::from("else"))
|
||||
{
|
||||
else_stmt = Some(self.stmt()?);
|
||||
}
|
||||
return Ok(Box::new(Node::If {
|
||||
condition,
|
||||
stmt,
|
||||
else_stmt,
|
||||
}));
|
||||
}
|
||||
"while" => {
|
||||
self.tokenizer.cursor += 1;
|
||||
self.tokenizer.expect(String::from("("))?;
|
||||
let condition = self.expr()?;
|
||||
self.tokenizer.expect(String::from(")"))?;
|
||||
let stmt = self.stmt()?;
|
||||
return Ok(Box::new(Node::While { condition, stmt }));
|
||||
}
|
||||
"for" => {
|
||||
self.tokenizer.cursor += 1;
|
||||
self.tokenizer.expect(String::from("("))?;
|
||||
let init: Option<Box<Node>> =
|
||||
if self.tokenizer.current_token().str != String::from(";") {
|
||||
Some(self.expr()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.tokenizer.expect(String::from(";"))?;
|
||||
|
||||
let condition: Option<Box<Node>> =
|
||||
if self.tokenizer.current_token().str != String::from(";") {
|
||||
Some(self.expr()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.tokenizer.expect(String::from(";"))?;
|
||||
|
||||
let update: Option<Box<Node>> =
|
||||
if self.tokenizer.current_token().str != String::from(")") {
|
||||
Some(self.expr()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.tokenizer.expect(String::from(")"))?;
|
||||
|
||||
let stmt = self.stmt()?;
|
||||
|
||||
return Ok(Box::new(Node::For {
|
||||
init,
|
||||
condition,
|
||||
update,
|
||||
stmt,
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let node = self.expr();
|
||||
self.tokenizer.expect(String::from(";"))?;
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn expr(&mut self) -> Result<Box<Node>, String> {
|
||||
Ok(self.assign()?)
|
||||
}
|
||||
|
||||
pub fn assign(&mut self) -> Result<Box<Node>, String> {
|
||||
let mut node = self.equality()?;
|
||||
|
||||
if self.tokenizer.consume(String::from("=")) {
|
||||
node = Node::new_node(NodeKind::ASSIGN, node, self.assign()?);
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
pub fn equality(&mut self) -> Result<Box<Node>, String> {
|
||||
let mut node = self.relational()?;
|
||||
|
||||
loop {
|
||||
if self.tokenizer.consume(String::from("==")) {
|
||||
node = Node::new_node(NodeKind::EQ, node, self.relational()?);
|
||||
} else if self.tokenizer.consume(String::from("!=")) {
|
||||
node = Node::new_node(NodeKind::NE, node, self.relational()?);
|
||||
} else {
|
||||
return Ok(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relational(&mut self) -> Result<Box<Node>, String> {
|
||||
let mut node = self.add()?;
|
||||
|
||||
loop {
|
||||
if self.tokenizer.consume(String::from("<=")) {
|
||||
node = Node::new_node(NodeKind::LE, node, self.add()?);
|
||||
} else if self.tokenizer.consume(String::from("<")) {
|
||||
node = Node::new_node(NodeKind::LT, node, self.add()?);
|
||||
} else if self.tokenizer.consume(String::from(">=")) {
|
||||
node = Node::new_node(NodeKind::LE, self.add()?, node);
|
||||
} else if self.tokenizer.consume(String::from(">")) {
|
||||
node = Node::new_node(NodeKind::LT, self.add()?, node);
|
||||
} else {
|
||||
return Ok(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self) -> Result<Box<Node>, String> {
|
||||
let mut node = self.mul()?;
|
||||
|
||||
loop {
|
||||
if self.tokenizer.consume(String::from("+")) {
|
||||
node = Node::new_node(NodeKind::ADD, node, self.mul()?);
|
||||
} else if self.tokenizer.consume(String::from("-")) {
|
||||
node = Node::new_node(NodeKind::SUB, node, self.mul()?);
|
||||
} else if self.tokenizer.consume(String::from("+=")) {
|
||||
node = Node::new_node(
|
||||
NodeKind::ASSIGN,
|
||||
Box::new((*node).clone()),
|
||||
Node::new_node(NodeKind::ADD, node, self.mul()?),
|
||||
);
|
||||
} else if self.tokenizer.consume(String::from("-=")) {
|
||||
node = Node::new_node(
|
||||
NodeKind::ASSIGN,
|
||||
Box::new((*node).clone()),
|
||||
Node::new_node(NodeKind::SUB, node, self.mul()?),
|
||||
);
|
||||
} else {
|
||||
return Ok(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul(&mut self) -> Result<Box<Node>, String> {
|
||||
let mut node = self.unary()?;
|
||||
loop {
|
||||
if self.tokenizer.consume(String::from("*")) {
|
||||
node = Node::new_node(NodeKind::MUL, node, self.unary()?);
|
||||
} else if self.tokenizer.consume(String::from("/")) {
|
||||
node = Node::new_node(NodeKind::DIV, node, self.unary()?);
|
||||
} else if self.tokenizer.consume(String::from("*=")) {
|
||||
node = Node::new_node(
|
||||
NodeKind::ASSIGN,
|
||||
Box::new((*node).clone()),
|
||||
Node::new_node(NodeKind::MUL, node, self.unary()?),
|
||||
);
|
||||
} else if self.tokenizer.consume(String::from("/=")) {
|
||||
node = Node::new_node(
|
||||
NodeKind::ASSIGN,
|
||||
Box::new((*node).clone()),
|
||||
Node::new_node(NodeKind::DIV, node, self.unary()?),
|
||||
);
|
||||
} else {
|
||||
return Ok(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary(&mut self) -> Result<Box<Node>, String> {
|
||||
if self.tokenizer.consume(String::from("(")) {
|
||||
let node = self.expr()?;
|
||||
self.tokenizer.expect(String::from(")"))?;
|
||||
return Ok(node);
|
||||
}
|
||||
|
||||
if self.tokenizer.current_token().kind == TokenKind::IDENT {
|
||||
let node = self.tokenizer.expect_ident()?;
|
||||
return Ok(Node::new_lvar_node(node.clone()));
|
||||
}
|
||||
|
||||
return Ok(Node::new_num_node(self.tokenizer.expect_number()?));
|
||||
}
|
||||
|
||||
pub fn unary(&mut self) -> Result<Box<Node>, String> {
|
||||
if self.tokenizer.consume(String::from("+")) {
|
||||
return Ok(self.primary()?);
|
||||
}
|
||||
if self.tokenizer.consume(String::from("-")) {
|
||||
return Ok(Node::new_node(
|
||||
NodeKind::SUB,
|
||||
Node::new_num_node(0),
|
||||
self.primary()?,
|
||||
));
|
||||
}
|
||||
return Ok(self.primary()?);
|
||||
}
|
||||
}
|
91
src/source.rs
Normal file
91
src/source.rs
Normal file
@ -0,0 +1,91 @@
|
||||
#[derive(Clone)]
|
||||
pub struct Source {
|
||||
pub src: Vec<char>,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn get_char(&mut self, check: impl Fn(char) -> bool) -> Result<char, String> {
|
||||
match self.get_next() {
|
||||
Ok(c) => {
|
||||
if check(c) {
|
||||
Ok(c)
|
||||
} else {
|
||||
self.pos -= 1;
|
||||
Err(String::from("Not found."))
|
||||
}
|
||||
}
|
||||
|
||||
Err(text) => Err(text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string(&mut self, string: String) -> Result<String, String> {
|
||||
let first_pos = self.pos;
|
||||
for i in 0..string.chars().count() {
|
||||
if self.has_next() {
|
||||
match self.get_next() {
|
||||
Ok(c) => {
|
||||
if c == string.chars().nth(i).unwrap() {
|
||||
continue;
|
||||
} else {
|
||||
self.pos = first_pos;
|
||||
return Err(String::from(""));
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
} else {
|
||||
self.pos = first_pos;
|
||||
return Err(String::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
pub fn get_chars(&mut self, check: impl Fn(char) -> bool) -> Result<String, String> {
|
||||
let mut buffer = String::from("");
|
||||
while self.has_next() {
|
||||
match self.get_next() {
|
||||
Ok(c) => {
|
||||
if check(c) {
|
||||
buffer += &c.to_string();
|
||||
} else {
|
||||
self.pos -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if buffer == "" {
|
||||
Err(String::from("Not found."))
|
||||
} else {
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next(&mut self) -> Result<char, String> {
|
||||
self.pos += 1;
|
||||
if self.src.len() > self.pos - 1 {
|
||||
Ok(self.src[self.pos - 1])
|
||||
} else {
|
||||
Err(String::from("EOF"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(src: String) -> Source {
|
||||
Source {
|
||||
src: src.chars().collect(),
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_next(&self) -> bool {
|
||||
self.src.len() > self.pos
|
||||
}
|
||||
}
|
16
src/token.rs
Normal file
16
src/token.rs
Normal file
@ -0,0 +1,16 @@
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum TokenKind {
|
||||
CONTROL,
|
||||
RETURN,
|
||||
RESERVED,
|
||||
IDENT,
|
||||
NUMBER,
|
||||
EOF,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Token {
|
||||
pub kind: TokenKind,
|
||||
pub num: usize,
|
||||
pub str: String,
|
||||
}
|
232
src/tokenizer.rs
Normal file
232
src/tokenizer.rs
Normal file
@ -0,0 +1,232 @@
|
||||
use crate::source::*;
|
||||
use crate::token::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tokenizer {
|
||||
pub tokens: Vec<Token>,
|
||||
pub cursor: usize,
|
||||
}
|
||||
impl Tokenizer {
|
||||
pub fn current_token(&mut self) -> &mut Token {
|
||||
&mut self.tokens[self.cursor]
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, op: String) -> bool {
|
||||
return if self.current_token().kind != TokenKind::RESERVED || self.current_token().str != op
|
||||
{
|
||||
false
|
||||
} else {
|
||||
self.cursor += 1;
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
pub fn consume_kind(&mut self, kind: TokenKind) -> bool {
|
||||
return if self.current_token().kind != kind {
|
||||
false
|
||||
} else {
|
||||
self.cursor += 1;
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
pub fn consume_kind_str(&mut self, kind: TokenKind, string: String) -> bool {
|
||||
return if self.current_token().kind == kind && self.current_token().str == string {
|
||||
self.cursor += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
}
|
||||
|
||||
pub fn expect(&mut self, op: String) -> Result<(), String> {
|
||||
if self.current_token().str != op {
|
||||
return Err(format!("Unexpected type : {}", op));
|
||||
}
|
||||
self.cursor += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn expect_kind(&mut self, kind: TokenKind) -> Result<(), String> {
|
||||
if self.current_token().kind != kind {
|
||||
return Err(format!("Unexpected token: {:?}", self.current_token().kind));
|
||||
}
|
||||
self.cursor += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn expect_ident(&mut self) -> Result<String, String> {
|
||||
if self.current_token().kind != TokenKind::IDENT {
|
||||
return Err(format!(
|
||||
"Unexpected type : {:?}",
|
||||
self.current_token().kind
|
||||
));
|
||||
}
|
||||
let val = self.current_token().str.clone();
|
||||
self.cursor += 1;
|
||||
Ok(val.to_string())
|
||||
}
|
||||
|
||||
pub fn expect_number(&mut self) -> Result<usize, String> {
|
||||
let kind = self.current_token().kind;
|
||||
if kind != TokenKind::NUMBER {
|
||||
return Err(format!("Unexpected type : {:?}", kind));
|
||||
}
|
||||
let val = self.current_token().num;
|
||||
self.cursor += 1;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn new() -> Tokenizer {
|
||||
Tokenizer {
|
||||
cursor: 0,
|
||||
tokens: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_reserved(op: String) -> Token {
|
||||
Token {
|
||||
kind: TokenKind::RESERVED,
|
||||
str: op,
|
||||
num: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_number(num: usize) -> Token {
|
||||
Token {
|
||||
kind: TokenKind::NUMBER,
|
||||
num: num,
|
||||
str: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tokenize(&mut self, source: &mut Source) -> Result<Vec<Token>, String> {
|
||||
let reserved: Vec<String> = vec![
|
||||
String::from("+="),
|
||||
String::from("-="),
|
||||
String::from("*="),
|
||||
String::from("/="),
|
||||
String::from("+"),
|
||||
String::from("-"),
|
||||
String::from("*"),
|
||||
String::from("/"),
|
||||
String::from("&&"),
|
||||
String::from("&"),
|
||||
String::from("{"),
|
||||
String::from("}"),
|
||||
String::from("("),
|
||||
String::from(")"),
|
||||
String::from("=="),
|
||||
String::from("!="),
|
||||
String::from(">="),
|
||||
String::from("<="),
|
||||
String::from("<"),
|
||||
String::from(">"),
|
||||
String::from("="),
|
||||
String::from(";"),
|
||||
String::from(":"),
|
||||
];
|
||||
|
||||
let controls: Vec<String> = vec![
|
||||
String::from("for"),
|
||||
String::from("while"),
|
||||
String::from("if"),
|
||||
String::from("else"),
|
||||
];
|
||||
|
||||
while source.has_next() {
|
||||
match source.get_char(is_whitespace) {
|
||||
Ok(_) => {
|
||||
continue;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
match contains_list_chars(source, reserved.clone()) {
|
||||
Ok(op) => {
|
||||
self.tokens
|
||||
.push(Tokenizer::create_reserved(String::from(op)));
|
||||
continue;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
match source.get_chars(is_digit) {
|
||||
Ok(num) => {
|
||||
self.tokens
|
||||
.push(Tokenizer::create_number(num.parse().unwrap()));
|
||||
continue;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
match source.get_chars(or(is_ascii_lowercase, or(is_digit, is('_')))) {
|
||||
Ok(c) => {
|
||||
if c == String::from("return") {
|
||||
self.tokens.push(Token {
|
||||
kind: TokenKind::RETURN,
|
||||
str: String::default(),
|
||||
num: 0,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if controls.contains(&c) {
|
||||
self.tokens.push(Token {
|
||||
kind: TokenKind::CONTROL,
|
||||
str: c,
|
||||
num: 0,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tokens.push(Token {
|
||||
kind: TokenKind::IDENT,
|
||||
str: c,
|
||||
num: 0,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
return Err(String::from("Failed to tokenize"));
|
||||
}
|
||||
|
||||
self.tokens.push(Token {
|
||||
kind: TokenKind::EOF,
|
||||
str: String::default(),
|
||||
num: 0,
|
||||
});
|
||||
|
||||
Ok(self.tokens.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_list_chars(source: &mut Source, list: Vec<String>) -> Result<String, String> {
|
||||
for target in list {
|
||||
match source.get_string(target) {
|
||||
Ok(string) => {
|
||||
return Ok(string);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
return Err(String::from(""));
|
||||
}
|
||||
|
||||
fn or(f: impl Fn(char) -> bool, g: impl Fn(char) -> bool) -> impl Fn(char) -> bool {
|
||||
move |c| f(c) || g(c)
|
||||
}
|
||||
|
||||
fn is(ch: char) -> impl Fn(char) -> bool {
|
||||
move |c| c == ch
|
||||
}
|
||||
|
||||
fn is_whitespace(c: char) -> bool {
|
||||
c.is_whitespace()
|
||||
}
|
||||
|
||||
fn is_digit(c: char) -> bool {
|
||||
c.is_digit(10)
|
||||
}
|
||||
|
||||
fn is_ascii_lowercase(c: char) -> bool {
|
||||
c.is_ascii_lowercase()
|
||||
}
|
14
src/variable.rs
Normal file
14
src/variable.rs
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Variable {
|
||||
Number {
|
||||
value: usize,
|
||||
},
|
||||
Text {
|
||||
value: String,
|
||||
},
|
||||
Return {
|
||||
value: Box<Variable>
|
||||
},
|
||||
None {}
|
||||
}
|
Reference in New Issue
Block a user