Moving to dmc-lib, keeping it smaller!
This commit is contained in:
parent
8ba79be920
commit
8df46eec8d
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -204,6 +204,10 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dmc-lib"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
|
||||
@ -25,4 +25,4 @@ cst-test-generator = { path = "cst-test-generator" }
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["ast-generator", "cst-test-generator"]
|
||||
members = ["ast-generator", "cst-test-generator", "dmc-lib"]
|
||||
|
||||
6
dmc-lib/Cargo.toml
Normal file
6
dmc-lib/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "dmc-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
80
dmc-lib/src/ast/call.rs
Normal file
80
dmc-lib/src/ast/call.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
pub struct Call {
|
||||
callee: Box<Expression>,
|
||||
arguments: Vec<Expression>,
|
||||
source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn new(callee: Expression, arguments: Vec<Expression>, source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
callee: callee.into(),
|
||||
arguments,
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn callee(&self) -> &Expression {
|
||||
&self.callee
|
||||
}
|
||||
|
||||
pub fn arguments(&self) -> Vec<&Expression> {
|
||||
self.arguments.iter().collect()
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
diagnostics.append(&mut self.callee.gather_declared_names(symbol_table));
|
||||
for argument in &mut self.arguments {
|
||||
diagnostics.append(&mut argument.gather_declared_names(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
diagnostics.append(&mut self.callee.check_name_usages(symbol_table));
|
||||
for argument in &mut self.arguments {
|
||||
diagnostics.append(&mut argument.check_name_usages(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
diagnostics.append(&mut self.callee.type_check(symbol_table));
|
||||
for argument in &mut self.arguments {
|
||||
diagnostics.append(&mut argument.type_check(symbol_table));
|
||||
}
|
||||
|
||||
// check that callee is callable
|
||||
match self.callee.type_info() {
|
||||
TypeInfo::Function(_) => {}
|
||||
_ => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
"Receiver is not callable",
|
||||
self.callee.source_range().start(),
|
||||
self.callee.source_range().end(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> TypeInfo {
|
||||
match self.callee.type_info() {
|
||||
TypeInfo::Function(function_symbol) => function_symbol.return_type(),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
}
|
||||
43
dmc-lib/src/ast/compilation_unit.rs
Normal file
43
dmc-lib/src/ast/compilation_unit.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::ast::function::Function;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
pub struct CompilationUnit {
|
||||
functions: Vec<Function>,
|
||||
}
|
||||
|
||||
impl CompilationUnit {
|
||||
pub fn new(functions: Vec<Function>) -> Self {
|
||||
Self { functions }
|
||||
}
|
||||
|
||||
pub fn functions(&self) -> Vec<&Function> {
|
||||
self.functions.iter().collect()
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
symbol_table.push_scope("compilation_unit_scope");
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.gather_declared_names(symbol_table));
|
||||
}
|
||||
symbol_table.pop_scope();
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.check_name_usages(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for function in &mut self.functions {
|
||||
diagnostics.append(&mut function.type_check(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
}
|
||||
58
dmc-lib/src/ast/expression.rs
Normal file
58
dmc-lib/src/ast/expression.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::ast::call::Call;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
pub enum Expression {
|
||||
Call(Call),
|
||||
IntegerLiteral(IntegerLiteral),
|
||||
String(StringLiteral),
|
||||
Identifier(Identifier),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Expression::Call(call) => call.gather_declared_names(symbol_table),
|
||||
Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Expression::Call(call) => call.check_name_usages(symbol_table),
|
||||
Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Expression::Call(call) => call.type_check(symbol_table),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> TypeInfo {
|
||||
match self {
|
||||
Expression::Call(call) => call.type_info(),
|
||||
Expression::IntegerLiteral(_) => TypeInfo::Integer,
|
||||
Expression::String(_) => TypeInfo::String,
|
||||
Expression::Identifier(identifier) => identifier.type_info(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
match self {
|
||||
Expression::Call(call) => call.source_range(),
|
||||
Expression::IntegerLiteral(integer_literal) => integer_literal.source_range(),
|
||||
Expression::String(string_literal) => string_literal.source_range(),
|
||||
Expression::Identifier(identifier) => identifier.source_range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
78
dmc-lib/src/ast/function.rs
Normal file
78
dmc-lib/src/ast/function.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
|
||||
pub struct Function {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(
|
||||
declared_name: &str,
|
||||
declared_name_source_range: SourceRange,
|
||||
statements: Vec<Statement>,
|
||||
) -> Self {
|
||||
Self {
|
||||
declared_name: declared_name.to_string(),
|
||||
declared_name_source_range,
|
||||
statements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn statements(&self) -> Vec<&Statement> {
|
||||
self.statements.iter().collect()
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
// insert function symbol
|
||||
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
|
||||
self.declared_name(),
|
||||
&vec![], // todo
|
||||
));
|
||||
if let Err(symbol_insert_error) = insert_result {
|
||||
match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Function {} already declared in current scope",
|
||||
already_declared.name()
|
||||
),
|
||||
self.declared_name_source_range.start(),
|
||||
self.declared_name_source_range.end(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
symbol_table.push_scope(&format!("function_scope({})", self.declared_name()));
|
||||
for statement in &mut self.statements {
|
||||
diagnostics.append(&mut statement.gather_declared_names(symbol_table));
|
||||
}
|
||||
symbol_table.pop_scope();
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for statement in &mut self.statements {
|
||||
diagnostics.append(&mut statement.check_name_usages(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
for statement in &mut self.statements {
|
||||
diagnostics.append(&mut statement.type_check(symbol_table));
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
}
|
||||
64
dmc-lib/src/ast/identifier.rs
Normal file
64
dmc-lib/src/ast/identifier.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::ExpressibleSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
pub struct Identifier {
|
||||
name: String,
|
||||
scope_id: Option<usize>,
|
||||
expressible_symbol: Option<ExpressibleSymbol>,
|
||||
source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
pub fn new(name: &str, source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
scope_id: None,
|
||||
expressible_symbol: None,
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
self.scope_id = Some(symbol_table.current_scope_id());
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let maybe_expressible_symbol =
|
||||
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
|
||||
match maybe_expressible_symbol {
|
||||
None => {
|
||||
vec![Diagnostic::new(
|
||||
&format!("Unable to resolve symbol {}", self.name),
|
||||
self.source_range.start(),
|
||||
self.source_range.end(),
|
||||
)]
|
||||
}
|
||||
Some(expressible_symbol) => {
|
||||
self.expressible_symbol = Some(expressible_symbol);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> TypeInfo {
|
||||
match self.expressible_symbol.as_ref().unwrap() {
|
||||
ExpressibleSymbol::Function(function_symbol) => {
|
||||
TypeInfo::Function(function_symbol.clone())
|
||||
}
|
||||
ExpressibleSymbol::Parameter(parameter_symbol) => parameter_symbol.type_info().clone(),
|
||||
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.type_info().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
}
|
||||
23
dmc-lib/src/ast/integer_literal.rs
Normal file
23
dmc-lib/src/ast/integer_literal.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
pub struct IntegerLiteral {
|
||||
value: i64,
|
||||
source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl IntegerLiteral {
|
||||
pub fn new(value: i64, source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
value,
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> i64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
}
|
||||
71
dmc-lib/src/ast/let_statement.rs
Normal file
71
dmc-lib/src/ast/let_statement.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::VariableSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
|
||||
pub struct LetStatement {
|
||||
declared_name: String,
|
||||
declared_name_source_range: SourceRange,
|
||||
initializer: Box<Expression>,
|
||||
}
|
||||
|
||||
impl LetStatement {
|
||||
pub fn new(
|
||||
declared_name: &str,
|
||||
declared_name_source_range: SourceRange,
|
||||
initializer: Expression,
|
||||
) -> Self {
|
||||
Self {
|
||||
declared_name: declared_name.to_string(),
|
||||
declared_name_source_range,
|
||||
initializer: initializer.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn initializer(&self) -> &Expression {
|
||||
&self.initializer
|
||||
}
|
||||
|
||||
pub fn initializer_mut(&mut self) -> &mut Expression {
|
||||
&mut self.initializer
|
||||
}
|
||||
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
self.initializer_mut().gather_declared_names(symbol_table);
|
||||
let insert_result = symbol_table.insert_variable_symbol(VariableSymbol::new(
|
||||
self.declared_name(),
|
||||
self.initializer().type_info().clone(),
|
||||
));
|
||||
if let Err(symbol_insert_error) = insert_result {
|
||||
match symbol_insert_error {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
"Symbol {} already declared in current scope",
|
||||
already_declared.name()
|
||||
),
|
||||
self.declared_name_source_range.start(),
|
||||
self.declared_name_source_range.end(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
self.initializer.check_name_usages(symbol_table)
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
diagnostics.append(&mut self.initializer.type_check(symbol_table));
|
||||
diagnostics
|
||||
}
|
||||
}
|
||||
43
dmc-lib/src/ast/mod.rs
Normal file
43
dmc-lib/src/ast/mod.rs
Normal file
@ -0,0 +1,43 @@
|
||||
pub mod call;
|
||||
pub mod compilation_unit;
|
||||
pub mod expression;
|
||||
pub mod function;
|
||||
pub mod identifier;
|
||||
pub mod integer_literal;
|
||||
pub mod let_statement;
|
||||
pub mod statement;
|
||||
pub mod string_literal;
|
||||
|
||||
#[cfg(test)]
|
||||
mod name_tests {
|
||||
use crate::parser::parse_compilation_unit;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
#[test]
|
||||
fn smoke_screen() {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut compilation_unit =
|
||||
parse_compilation_unit("fn println() end fn main() let x = 42 println(x) end");
|
||||
assert_eq!(
|
||||
compilation_unit
|
||||
.gather_declared_names(&mut symbol_table)
|
||||
.len(),
|
||||
0
|
||||
);
|
||||
assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_some_diagnostics() {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut compilation_unit = parse_compilation_unit("fn main() notDefined(uhOh) end");
|
||||
assert_eq!(
|
||||
compilation_unit
|
||||
.gather_declared_names(&mut symbol_table)
|
||||
.len(),
|
||||
0
|
||||
);
|
||||
let name_usage_diagnostics = compilation_unit.check_name_usages(&symbol_table);
|
||||
assert_eq!(name_usage_diagnostics.len(), 2);
|
||||
}
|
||||
}
|
||||
32
dmc-lib/src/ast/statement.rs
Normal file
32
dmc-lib/src/ast/statement.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
pub enum Statement {
|
||||
Let(LetStatement),
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table),
|
||||
Statement::Expression(expression) => expression.gather_declared_names(symbol_table),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table),
|
||||
Statement::Expression(expression) => expression.check_name_usages(symbol_table),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||
match self {
|
||||
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
||||
Statement::Expression(expression) => expression.type_check(symbol_table),
|
||||
}
|
||||
}
|
||||
}
|
||||
23
dmc-lib/src/ast/string_literal.rs
Normal file
23
dmc-lib/src/ast/string_literal.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
pub struct StringLiteral {
|
||||
content: String,
|
||||
source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl StringLiteral {
|
||||
pub fn new(content: &str, source_range: SourceRange) -> Self {
|
||||
Self {
|
||||
content: content.into(),
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &str {
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
}
|
||||
28
dmc-lib/src/diagnostic.rs
Normal file
28
dmc-lib/src/diagnostic.rs
Normal file
@ -0,0 +1,28 @@
|
||||
#[derive(Debug)]
|
||||
pub struct Diagnostic {
|
||||
message: String,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
pub fn new(message: &str, start: usize, end: usize) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
start,
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
pub fn start(&self) -> usize {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn end(&self) -> usize {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
157
dmc-lib/src/lexer.rs
Normal file
157
dmc-lib/src/lexer.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use crate::token::{Token, TokenKind};
|
||||
|
||||
pub struct Lexer<'a> {
|
||||
input: &'a str,
|
||||
position: usize,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
Self { input, position: 0 }
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Result<Token, LexerError>> {
|
||||
let maybe_chunk = self.input.get(self.position..);
|
||||
if maybe_chunk.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut chunk = maybe_chunk.unwrap();
|
||||
if chunk.is_empty() {
|
||||
// in case we're done
|
||||
return None;
|
||||
}
|
||||
while chunk.starts_with(&[' ', '\t', '\r', '\n']) {
|
||||
// ignore whitespace
|
||||
self.position += 1;
|
||||
let maybe_chunk = self.input.get(self.position..);
|
||||
if maybe_chunk.is_none() {
|
||||
return None;
|
||||
} else {
|
||||
chunk = maybe_chunk.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let token = if chunk.starts_with("(") {
|
||||
Token::new(self.position, self.position + 1, TokenKind::LeftParentheses)
|
||||
} else if chunk.starts_with(")") {
|
||||
Token::new(
|
||||
self.position,
|
||||
self.position + 1,
|
||||
TokenKind::RightParentheses,
|
||||
)
|
||||
} else if chunk.starts_with("=") {
|
||||
Token::new(self.position, self.position + 1, TokenKind::Equals)
|
||||
} else {
|
||||
// more than one char token
|
||||
if chunk.starts_with(|c: char| c.is_ascii_digit()) {
|
||||
// number literal
|
||||
let mut end = self.position;
|
||||
for char in chunk.chars() {
|
||||
if char.is_ascii_digit() {
|
||||
end += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Token::new(self.position, end, TokenKind::IntegerLiteral)
|
||||
} else if chunk.starts_with("\"") {
|
||||
// string literal
|
||||
let mut end = self.position;
|
||||
let mut terminated = false;
|
||||
let mut chars = chunk.chars();
|
||||
chars.next(); // skip opening quote
|
||||
end += 1;
|
||||
for char in chars {
|
||||
end += 1;
|
||||
if char == '"' {
|
||||
terminated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !terminated {
|
||||
return Some(Err(LexerError::new(LexerErrorKind::UnterminatedString)));
|
||||
}
|
||||
Token::new(self.position, end, TokenKind::String)
|
||||
} else {
|
||||
// keyword or identifier
|
||||
let mut prefix = String::new();
|
||||
for char in chunk.chars() {
|
||||
if char.is_alphanumeric() || char == '_' {
|
||||
prefix.push(char);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let token_kind = match prefix.as_str() {
|
||||
"fn" => TokenKind::Fn,
|
||||
"end" => TokenKind::End,
|
||||
"let" => TokenKind::Let,
|
||||
_ => TokenKind::Identifier,
|
||||
};
|
||||
Token::new(self.position, self.position + prefix.len(), token_kind)
|
||||
}
|
||||
};
|
||||
self.position += token.end() - token.start();
|
||||
Some(Ok(token))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct LexerError {
|
||||
kind: LexerErrorKind,
|
||||
}
|
||||
|
||||
impl LexerError {
|
||||
pub fn new(kind: LexerErrorKind) -> Self {
|
||||
Self { kind }
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> LexerErrorKind {
|
||||
self.kind
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum LexerErrorKind {
|
||||
UnterminatedString,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_next(lexer: &mut Lexer, kind: TokenKind, length: usize) {
|
||||
let token = lexer.next().unwrap().unwrap();
|
||||
println!("{:?}", token);
|
||||
assert_eq!(token.kind(), kind);
|
||||
assert_eq!(token.end() - token.start(), length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forty_two() {
|
||||
let mut lexer = Lexer::new("fn main() 42 end");
|
||||
assert_next(&mut lexer, TokenKind::Fn, 2);
|
||||
assert_next(&mut lexer, TokenKind::Identifier, 4);
|
||||
assert_next(&mut lexer, TokenKind::LeftParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::RightParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::IntegerLiteral, 2);
|
||||
assert_next(&mut lexer, TokenKind::End, 3);
|
||||
assert_eq!(lexer.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let mut lexer = Lexer::new("fn main() println(\"Hello, World!\") end");
|
||||
assert_next(&mut lexer, TokenKind::Fn, 2);
|
||||
assert_next(&mut lexer, TokenKind::Identifier, 4);
|
||||
assert_next(&mut lexer, TokenKind::LeftParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::RightParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::Identifier, 7);
|
||||
assert_next(&mut lexer, TokenKind::LeftParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::String, 15);
|
||||
assert_next(&mut lexer, TokenKind::RightParentheses, 1);
|
||||
assert_next(&mut lexer, TokenKind::End, 3);
|
||||
assert_eq!(lexer.next(), None);
|
||||
}
|
||||
}
|
||||
10
dmc-lib/src/lib.rs
Normal file
10
dmc-lib/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
mod ast;
|
||||
mod diagnostic;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod scope;
|
||||
mod source_range;
|
||||
mod symbol;
|
||||
mod symbol_table;
|
||||
mod token;
|
||||
mod type_info;
|
||||
279
dmc-lib/src/parser.rs
Normal file
279
dmc-lib/src/parser.rs
Normal file
@ -0,0 +1,279 @@
|
||||
use crate::ast::call::Call;
|
||||
use crate::ast::compilation_unit::CompilationUnit;
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::ast::function::Function;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::token::{Token, TokenKind};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn parse_compilation_unit(input: &str) -> CompilationUnit {
|
||||
let mut parser = Parser::new(input);
|
||||
parser.compilation_unit()
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
input: &'a str,
|
||||
lexer: Lexer<'a>,
|
||||
current: Option<Token>,
|
||||
lookahead: Option<Token>,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn new(input: &'a str) -> Self {
|
||||
Self {
|
||||
input,
|
||||
lexer: Lexer::new(input),
|
||||
current: None,
|
||||
lookahead: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
if self.lookahead.is_some() {
|
||||
// we've advanced at least once
|
||||
self.current = self.lookahead.take();
|
||||
self.lookahead = match self.lexer.next() {
|
||||
None => None,
|
||||
Some(result) => match result {
|
||||
Ok(token) => Some(token),
|
||||
Err(lexer_error) => {
|
||||
panic!("{:?}", lexer_error);
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// we've not yet advanced, so fetch both
|
||||
// current
|
||||
match self.lexer.next() {
|
||||
None => {}
|
||||
Some(result) => match result {
|
||||
Ok(token) => {
|
||||
self.current = Some(token);
|
||||
}
|
||||
Err(lexer_error) => {
|
||||
panic!("{:?}", lexer_error);
|
||||
}
|
||||
},
|
||||
}
|
||||
// lookahead
|
||||
match self.lexer.next() {
|
||||
None => {}
|
||||
Some(result) => match result {
|
||||
Ok(token) => {
|
||||
self.lookahead = Some(token);
|
||||
}
|
||||
Err(lexer_error) => {
|
||||
panic!("{:?}", lexer_error);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_advance(&mut self, token_kind: TokenKind) -> Token {
|
||||
match self.current.take() {
|
||||
None => {
|
||||
panic!("Expected {:?} but found end of input", token_kind);
|
||||
}
|
||||
Some(token) => {
|
||||
if token.kind() == token_kind {
|
||||
self.advance();
|
||||
token
|
||||
} else {
|
||||
panic!(
|
||||
"Expected {:?} but found {:?} at {}",
|
||||
token_kind,
|
||||
token.kind(),
|
||||
token.start()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_current(&self, token_kind: TokenKind) -> bool {
|
||||
match &self.current {
|
||||
None => false,
|
||||
Some(token) => token.kind() == token_kind,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current(&self) -> &Token {
|
||||
match &self.current {
|
||||
None => {
|
||||
panic!("Unexpected end of input");
|
||||
}
|
||||
Some(token) => token,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_input(&self, start: usize, end: usize) -> &'a str {
|
||||
&self.input[start..end]
|
||||
}
|
||||
|
||||
fn token_text(&self, token: &Token) -> &'a str {
|
||||
self.sample_input(token.start(), token.end())
|
||||
}
|
||||
|
||||
pub fn compilation_unit(&mut self) -> CompilationUnit {
|
||||
let mut functions = vec![];
|
||||
self.advance();
|
||||
while self.current.is_some() {
|
||||
functions.push(self.function());
|
||||
}
|
||||
CompilationUnit::new(functions)
|
||||
}
|
||||
|
||||
fn function(&mut self) -> Function {
|
||||
self.expect_advance(TokenKind::Fn);
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier);
|
||||
self.expect_advance(TokenKind::LeftParentheses);
|
||||
// add params
|
||||
self.expect_advance(TokenKind::RightParentheses);
|
||||
let mut statements = vec![];
|
||||
while !self.peek_current(TokenKind::End) {
|
||||
statements.push(self.statement());
|
||||
}
|
||||
self.expect_advance(TokenKind::End);
|
||||
Function::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
statements,
|
||||
)
|
||||
}
|
||||
|
||||
fn statement(&mut self) -> Statement {
|
||||
let current = self.get_current();
|
||||
match current.kind() {
|
||||
TokenKind::Let => self.let_statement(),
|
||||
_ => self.expression_statement(),
|
||||
}
|
||||
}
|
||||
|
||||
fn let_statement(&mut self) -> Statement {
|
||||
self.expect_advance(TokenKind::Let);
|
||||
let identifier = self.expect_advance(TokenKind::Identifier);
|
||||
self.expect_advance(TokenKind::Equals);
|
||||
let expression = self.expression();
|
||||
Statement::Let(LetStatement::new(
|
||||
self.token_text(&identifier),
|
||||
SourceRange::new(identifier.start(), identifier.end()),
|
||||
expression,
|
||||
))
|
||||
}
|
||||
|
||||
fn expression_statement(&mut self) -> Statement {
|
||||
Statement::Expression(self.expression())
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> Expression {
|
||||
let current = self.get_current();
|
||||
let mut result = match current.kind() {
|
||||
TokenKind::IntegerLiteral => {
|
||||
let raw = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(
|
||||
i64::from_str(raw).unwrap(),
|
||||
source_range,
|
||||
))
|
||||
}
|
||||
TokenKind::String => {
|
||||
let with_quotes = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::String(StringLiteral::new(
|
||||
&with_quotes[1..with_quotes.len() - 1],
|
||||
source_range,
|
||||
))
|
||||
}
|
||||
TokenKind::Identifier => {
|
||||
let declared_name = self.token_text(current);
|
||||
let source_range = SourceRange::new(current.start(), current.end());
|
||||
self.advance();
|
||||
Expression::Identifier(Identifier::new(declared_name, source_range))
|
||||
}
|
||||
_ => panic!("Unexpected token {:?}", current.kind()),
|
||||
};
|
||||
|
||||
// postfixes
|
||||
while let Some(current) = &self.current {
|
||||
match current.kind() {
|
||||
TokenKind::LeftParentheses => {
|
||||
result = Expression::Call(self.call(result));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn call(&mut self, callee: Expression) -> Call {
|
||||
self.expect_advance(TokenKind::LeftParentheses);
|
||||
let mut arguments = vec![];
|
||||
while !self.peek_current(TokenKind::RightParentheses) {
|
||||
arguments.push(self.expression());
|
||||
}
|
||||
let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses);
|
||||
let source_range =
|
||||
SourceRange::new(callee.source_range().start(), right_parentheses_token.end());
|
||||
Call::new(callee, arguments, source_range)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod smoke_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn forty_two() {
|
||||
parse_compilation_unit("fn main() 42 end");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let compilation_unit = parse_compilation_unit("fn main() println(\"Hello, World!\") end");
|
||||
let functions = compilation_unit.functions();
|
||||
assert_eq!(functions.len(), 1);
|
||||
let function = functions[0];
|
||||
assert_eq!(function.declared_name(), "main");
|
||||
let statements = function.statements();
|
||||
assert_eq!(statements.len(), 1);
|
||||
if let Statement::Expression(expression) = statements[0] {
|
||||
if let Expression::Call(call) = expression {
|
||||
let callee = call.callee();
|
||||
match callee {
|
||||
Expression::Identifier(identifier) => {
|
||||
assert_eq!(identifier.name(), "println");
|
||||
}
|
||||
_ => panic!("Expected identifier"),
|
||||
}
|
||||
let arguments = call.arguments();
|
||||
assert_eq!(arguments.len(), 1);
|
||||
let first_argument = arguments[0];
|
||||
match first_argument {
|
||||
Expression::String(s) => {
|
||||
assert_eq!(s.content(), "Hello, World!");
|
||||
}
|
||||
_ => panic!("Expected string"),
|
||||
}
|
||||
} else {
|
||||
panic!("Expected call");
|
||||
}
|
||||
} else {
|
||||
panic!("Expected expression");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chained_calls() {
|
||||
parse_compilation_unit("fn main() getCl()() end");
|
||||
}
|
||||
}
|
||||
51
dmc-lib/src/scope.rs
Normal file
51
dmc-lib/src/scope.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use crate::symbol::{FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Scope {
|
||||
debug_name: String,
|
||||
parent_id: Option<usize>,
|
||||
function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||
parameter_symbols: HashMap<Rc<str>, Rc<ParameterSymbol>>,
|
||||
variable_symbols: HashMap<Rc<str>, Rc<VariableSymbol>>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(debug_name: &str, parent_id: Option<usize>) -> Self {
|
||||
Self {
|
||||
debug_name: debug_name.into(),
|
||||
parent_id,
|
||||
function_symbols: HashMap::new(),
|
||||
parameter_symbols: HashMap::new(),
|
||||
variable_symbols: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
|
||||
&self.function_symbols
|
||||
}
|
||||
|
||||
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<FunctionSymbol>> {
|
||||
&mut self.function_symbols
|
||||
}
|
||||
|
||||
pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||
&self.parameter_symbols
|
||||
}
|
||||
|
||||
pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||
&mut self.parameter_symbols
|
||||
}
|
||||
|
||||
pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||
&self.variable_symbols
|
||||
}
|
||||
|
||||
pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||
&mut self.variable_symbols
|
||||
}
|
||||
|
||||
pub fn parent_id(&self) -> Option<usize> {
|
||||
self.parent_id
|
||||
}
|
||||
}
|
||||
18
dmc-lib/src/source_range.rs
Normal file
18
dmc-lib/src/source_range.rs
Normal file
@ -0,0 +1,18 @@
|
||||
pub struct SourceRange {
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl SourceRange {
|
||||
pub fn new(start: usize, end: usize) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
pub fn start(&self) -> usize {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn end(&self) -> usize {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
90
dmc-lib/src/symbol.rs
Normal file
90
dmc-lib/src/symbol.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct FunctionSymbol {
|
||||
name: Rc<str>,
|
||||
parameters: Vec<Rc<ParameterSymbol>>,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn new(name: &str, parameters: &[Rc<ParameterSymbol>]) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
parameters: parameters.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> TypeInfo {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParameterSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
}
|
||||
|
||||
impl ParameterSymbol {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VariableSymbol {
|
||||
name: Rc<str>,
|
||||
type_info: TypeInfo,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
pub fn new(name: &str, type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
type_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn type_info(&self) -> &TypeInfo {
|
||||
&self.type_info
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ExpressibleSymbol {
|
||||
Function(Rc<FunctionSymbol>),
|
||||
Parameter(Rc<ParameterSymbol>),
|
||||
Variable(Rc<VariableSymbol>),
|
||||
}
|
||||
146
dmc-lib/src/symbol_table.rs
Normal file
146
dmc-lib/src/symbol_table.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use crate::scope::Scope;
|
||||
use crate::symbol::{ExpressibleSymbol, FunctionSymbol, ParameterSymbol, VariableSymbol};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct SymbolTable {
|
||||
scopes: Vec<Scope>,
|
||||
current_scope_id: Option<usize>,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scopes: vec![],
|
||||
current_scope_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_scope(&mut self, debug_name: &str) -> usize {
|
||||
let scope_id = self.scopes.len();
|
||||
let parent_id = self.current_scope_id;
|
||||
let scope = Scope::new(debug_name, parent_id);
|
||||
self.scopes.push(scope);
|
||||
self.current_scope_id = Some(scope_id);
|
||||
scope_id
|
||||
}
|
||||
|
||||
pub fn pop_scope(&mut self) {
|
||||
self.current_scope_id = self.current_scope().parent_id();
|
||||
}
|
||||
|
||||
pub fn current_scope_id(&self) -> usize {
|
||||
self.current_scope_id.unwrap()
|
||||
}
|
||||
|
||||
fn current_scope(&self) -> &Scope {
|
||||
&self.scopes[self.current_scope_id.unwrap()]
|
||||
}
|
||||
|
||||
fn current_scope_mut(&mut self) -> &mut Scope {
|
||||
&mut self.scopes[self.current_scope_id.unwrap()]
|
||||
}
|
||||
|
||||
fn current_scope_has_name(&self, name: &str) -> bool {
|
||||
let current_scope = self.current_scope();
|
||||
current_scope.function_symbols().contains_key(name)
|
||||
|| current_scope.parameter_symbols().contains_key(name)
|
||||
|| current_scope.variable_symbols().contains_key(name)
|
||||
}
|
||||
|
||||
pub fn insert_function_symbol(
|
||||
&mut self,
|
||||
function_symbol: FunctionSymbol,
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
if self.current_scope_has_name(function_symbol.name()) {
|
||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||
function_symbol.name(),
|
||||
)));
|
||||
}
|
||||
self.current_scope_mut()
|
||||
.function_symbols_mut()
|
||||
.insert(function_symbol.name_owned(), Rc::new(function_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_parameter_symbol(
|
||||
&mut self,
|
||||
parameter_symbol: ParameterSymbol,
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
if self.current_scope_has_name(parameter_symbol.name()) {
|
||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||
parameter_symbol.name(),
|
||||
)));
|
||||
}
|
||||
self.current_scope_mut()
|
||||
.parameter_symbols_mut()
|
||||
.insert(parameter_symbol.name_owned(), Rc::new(parameter_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_variable_symbol(
|
||||
&mut self,
|
||||
variable_symbol: VariableSymbol,
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
if self.current_scope_has_name(variable_symbol.name()) {
|
||||
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||
variable_symbol.name(),
|
||||
)));
|
||||
}
|
||||
self.current_scope_mut()
|
||||
.variable_symbols_mut()
|
||||
.insert(variable_symbol.name_owned(), Rc::new(variable_symbol));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_expressible_symbol(
|
||||
&self,
|
||||
scope_id: usize,
|
||||
name: &str,
|
||||
) -> Option<ExpressibleSymbol> {
|
||||
let mut maybe_scope = self.scopes.get(scope_id);
|
||||
if maybe_scope.is_none() {
|
||||
panic!("Invalid scope_id: {}", scope_id);
|
||||
}
|
||||
while let Some(scope) = maybe_scope {
|
||||
let maybe_expressible_symbol = scope
|
||||
.variable_symbols()
|
||||
.get(name)
|
||||
.map(|variable_symbol| ExpressibleSymbol::Variable(variable_symbol.clone()))
|
||||
.or_else(|| {
|
||||
scope
|
||||
.function_symbols()
|
||||
.get(name)
|
||||
.map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone()))
|
||||
})
|
||||
.or_else(|| {
|
||||
scope.parameter_symbols().get(name).map(|parameter_symbol| {
|
||||
ExpressibleSymbol::Parameter(parameter_symbol.clone())
|
||||
})
|
||||
});
|
||||
if maybe_expressible_symbol.is_some() {
|
||||
return maybe_expressible_symbol;
|
||||
} else {
|
||||
maybe_scope = scope.parent_id().map(|id| &self.scopes[id]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SymbolInsertError {
|
||||
AlreadyDeclared(AlreadyDeclared),
|
||||
}
|
||||
|
||||
pub struct AlreadyDeclared {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl AlreadyDeclared {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self { name: name.into() }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
38
dmc-lib/src/token.rs
Normal file
38
dmc-lib/src/token.rs
Normal file
@ -0,0 +1,38 @@
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct Token {
|
||||
start: usize,
|
||||
end: usize,
|
||||
kind: TokenKind,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn new(start: usize, end: usize, kind: TokenKind) -> Self {
|
||||
Self { start, end, kind }
|
||||
}
|
||||
|
||||
pub fn start(&self) -> usize {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn end(&self) -> usize {
|
||||
self.end
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> TokenKind {
|
||||
self.kind
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum TokenKind {
|
||||
Fn,
|
||||
Identifier,
|
||||
LeftParentheses,
|
||||
RightParentheses,
|
||||
End,
|
||||
Let,
|
||||
Equals,
|
||||
IntegerLiteral,
|
||||
LongLiteral,
|
||||
String,
|
||||
}
|
||||
9
dmc-lib/src/type_info.rs
Normal file
9
dmc-lib/src/type_info.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::symbol::FunctionSymbol;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TypeInfo {
|
||||
Integer,
|
||||
String,
|
||||
Function(Rc<FunctionSymbol>),
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user