Add Path parsing, AST node, sketching semantic analysis logic. WIP.
This commit is contained in:
parent
2dfd8e8a29
commit
347a9ec9c9
@ -5,6 +5,7 @@ use crate::ast::double_literal::DoubleLiteral;
|
|||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::integer_literal::IntegerLiteral;
|
use crate::ast::integer_literal::IntegerLiteral;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ pub enum Expression {
|
|||||||
Binary(BinaryExpression),
|
Binary(BinaryExpression),
|
||||||
Negative(NegativeExpression),
|
Negative(NegativeExpression),
|
||||||
Call(Call),
|
Call(Call),
|
||||||
|
Path(Path),
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
Integer(IntegerLiteral),
|
Integer(IntegerLiteral),
|
||||||
Double(DoubleLiteral),
|
Double(DoubleLiteral),
|
||||||
@ -24,6 +26,7 @@ impl Expression {
|
|||||||
Expression::Binary(binary_expression) => binary_expression.node_id(),
|
Expression::Binary(binary_expression) => binary_expression.node_id(),
|
||||||
Expression::Negative(negative_expression) => negative_expression.node_id(),
|
Expression::Negative(negative_expression) => negative_expression.node_id(),
|
||||||
Expression::Call(call) => call.node_id(),
|
Expression::Call(call) => call.node_id(),
|
||||||
|
Expression::Path(path) => path.node_id(),
|
||||||
Expression::Identifier(identifier) => identifier.node_id(),
|
Expression::Identifier(identifier) => identifier.node_id(),
|
||||||
Expression::Integer(integer_literal) => integer_literal.node_id(),
|
Expression::Integer(integer_literal) => integer_literal.node_id(),
|
||||||
Expression::Double(double_literal) => double_literal.node_id(),
|
Expression::Double(double_literal) => double_literal.node_id(),
|
||||||
@ -36,6 +39,7 @@ impl Expression {
|
|||||||
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
||||||
Expression::Negative(negative_expression) => negative_expression.source_range(),
|
Expression::Negative(negative_expression) => negative_expression.source_range(),
|
||||||
Expression::Call(call) => call.source_range(),
|
Expression::Call(call) => call.source_range(),
|
||||||
|
Expression::Path(path) => path.source_range(),
|
||||||
Expression::Identifier(identifier) => identifier.source_range(),
|
Expression::Identifier(identifier) => identifier.source_range(),
|
||||||
Expression::Integer(integer_literal) => integer_literal.source_range(),
|
Expression::Integer(integer_literal) => integer_literal.source_range(),
|
||||||
Expression::Double(double_literal) => double_literal.source_range(),
|
Expression::Double(double_literal) => double_literal.source_range(),
|
||||||
|
|||||||
@ -16,6 +16,7 @@ pub mod integer_literal;
|
|||||||
pub mod let_statement;
|
pub mod let_statement;
|
||||||
pub mod negative_expression;
|
pub mod negative_expression;
|
||||||
pub mod parameter;
|
pub mod parameter;
|
||||||
|
pub mod path;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod string_literal;
|
pub mod string_literal;
|
||||||
pub mod type_use;
|
pub mod type_use;
|
||||||
|
|||||||
43
dmc-lib/src/ast/path.rs
Normal file
43
dmc-lib/src/ast/path.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
|
||||||
|
pub struct Path {
|
||||||
|
node_id: NodeId,
|
||||||
|
source_range: SourceRange,
|
||||||
|
base: Box<Expression>,
|
||||||
|
identifier: Box<Identifier>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
pub fn new(
|
||||||
|
node_id: NodeId,
|
||||||
|
source_range: SourceRange,
|
||||||
|
base: Expression,
|
||||||
|
identifier: Identifier,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
node_id,
|
||||||
|
source_range,
|
||||||
|
base: base.into(),
|
||||||
|
identifier: identifier.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
|
&self.source_range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base(&self) -> &Expression {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identifier(&self) -> &Identifier {
|
||||||
|
&self.identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -439,6 +439,9 @@ fn lower_expression_to_ir_operation(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
||||||
|
Expression::Path(_path) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
let identifier_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
let identifier_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||||
let identifier_ir_variable =
|
let identifier_ir_variable =
|
||||||
@ -533,6 +536,9 @@ fn lower_expression_to_ir_expression(
|
|||||||
// return an expression referencing the temp var
|
// return an expression referencing the temp var
|
||||||
IrExpression::Variable(t_var_ir_variable)
|
IrExpression::Variable(t_var_ir_variable)
|
||||||
}
|
}
|
||||||
|
Expression::Path(_path) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
let rhs_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
let rhs_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||||
if let Some(rhs_ir_variable) =
|
if let Some(rhs_ir_variable) =
|
||||||
|
|||||||
@ -18,6 +18,7 @@ use crate::ast::integer_literal::IntegerLiteral;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
@ -166,21 +167,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance_until(&mut self, token_kinds: &[TokenKind]) {
|
|
||||||
while self.current.is_some() {
|
|
||||||
match &self.current {
|
|
||||||
None => {
|
|
||||||
// reached eoi
|
|
||||||
}
|
|
||||||
Some(current) => {
|
|
||||||
if token_kinds.contains(¤t.kind()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn advance(&mut self) {
|
fn advance(&mut self) {
|
||||||
fn fetch(lexer: &mut Lexer, diagnostics: &mut Diagnostics) -> Option<Token> {
|
fn fetch(lexer: &mut Lexer, diagnostics: &mut Diagnostics) -> Option<Token> {
|
||||||
let mut maybe_token: Option<Token> = None;
|
let mut maybe_token: Option<Token> = None;
|
||||||
@ -1038,6 +1024,21 @@ impl<'a> Parser<'a> {
|
|||||||
TokenKind::LeftParentheses => {
|
TokenKind::LeftParentheses => {
|
||||||
expression = Expression::Call(self.call(expression));
|
expression = Expression::Call(self.call(expression));
|
||||||
}
|
}
|
||||||
|
TokenKind::Dot => {
|
||||||
|
self.advance(); // .
|
||||||
|
if let Some(identifier) = self.identifier() {
|
||||||
|
let source_range = SourceRange::new(
|
||||||
|
expression.source_range().start(),
|
||||||
|
identifier.source_range().end(),
|
||||||
|
);
|
||||||
|
expression = Expression::Path(Path::new(
|
||||||
|
self.next_node_id(),
|
||||||
|
source_range,
|
||||||
|
expression,
|
||||||
|
identifier,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,6 +1048,18 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn identifier(&mut self) -> Option<Identifier> {
|
||||||
|
if let Some(identifier_token) = self.expect_advance(TokenKind::Identifier) {
|
||||||
|
Some(Identifier::new(
|
||||||
|
self.next_node_id(),
|
||||||
|
self.token_text(&identifier_token),
|
||||||
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_base(&mut self) -> Option<Expression> {
|
fn expression_base(&mut self) -> Option<Expression> {
|
||||||
let current = match self.current.as_ref() {
|
let current = match self.current.as_ref() {
|
||||||
Some(current) => current,
|
Some(current) => current,
|
||||||
@ -1350,6 +1363,21 @@ mod smoke_tests {
|
|||||||
fn class_with_generic_param() {
|
fn class_with_generic_param() {
|
||||||
smoke_test("class Foo<T> end");
|
smoke_test("class Foo<T> end");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_member_access() {
|
||||||
|
smoke_test("fn main(foo: Foo) foo.bar end")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_member_access() {
|
||||||
|
smoke_test("fn main(foo: Foo) foo.bar.baz end")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_member() {
|
||||||
|
smoke_test("fn main(s: String) s.len() end")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -9,11 +9,10 @@ use crate::ast::function::Function;
|
|||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
|
||||||
/// N.b.: the parent_scope_id refers to the **parent** of the compilation unit scope, and must
|
|
||||||
/// be a valid (non-panicking) index of `scopes`.
|
|
||||||
pub fn collect_scopes_compilation_unit(
|
pub fn collect_scopes_compilation_unit(
|
||||||
compilation_unit: &CompilationUnit,
|
compilation_unit: &CompilationUnit,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut AnalysisContext,
|
||||||
@ -112,6 +111,9 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext)
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
collect_scopes_call(call, ctx);
|
collect_scopes_call(call, ctx);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
collect_scopes_path(path, ctx);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
collect_scopes_identifier(identifier, ctx);
|
collect_scopes_identifier(identifier, ctx);
|
||||||
}
|
}
|
||||||
@ -143,6 +145,11 @@ fn collect_scopes_call(call: &Call, ctx: &mut AnalysisContext) {
|
|||||||
collect_scopes_expression(call.callee(), ctx);
|
collect_scopes_expression(call.callee(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_path(path: &Path, ctx: &mut AnalysisContext) {
|
||||||
|
collect_scopes_expression(path.base(), ctx);
|
||||||
|
// do not need to collect a scope for the identifier, since that is looked up via the base
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
||||||
ctx.associate_node_to_current_scope(identifier.node_id());
|
ctx.associate_node_to_current_scope(identifier.node_id());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use crate::ast::identifier::Identifier;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::diagnostic::Diagnostics;
|
use crate::diagnostic::Diagnostics;
|
||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
@ -195,6 +196,9 @@ fn resolve_names_expression(
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
resolve_names_call(call, ctx, diagnostics, phase);
|
resolve_names_call(call, ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
resolve_names_path(path, ctx, diagnostics, phase);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
resolve_names_identifier(identifier, ctx, diagnostics, phase);
|
resolve_names_identifier(identifier, ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
@ -235,6 +239,18 @@ fn resolve_names_call(
|
|||||||
resolve_names_expression(call.callee(), ctx, diagnostics, phase);
|
resolve_names_expression(call.callee(), ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_names_path(
|
||||||
|
path: &Path,
|
||||||
|
ctx: &mut AnalysisContext,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
// resolve base (left part)
|
||||||
|
resolve_names_expression(path.base(), ctx, diagnostics, phase);
|
||||||
|
// We cannot yet determine if the path's identifier (right part) is a valid memeber of the left,
|
||||||
|
// since we don't have type information yet. Therefore, it is deferred until type-checking.
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_names_identifier(
|
fn resolve_names_identifier(
|
||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut AnalysisContext,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use crate::ast::identifier::Identifier;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
||||||
@ -138,6 +139,9 @@ fn resolve_types_expression(
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
resolve_types_call(call, ctx, diagnostics);
|
resolve_types_call(call, ctx, diagnostics);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
resolve_types_path(path, ctx, diagnostics);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
resolve_types_identifier(identifier, ctx);
|
resolve_types_identifier(identifier, ctx);
|
||||||
}
|
}
|
||||||
@ -300,6 +304,33 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_types_path(path: &Path, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) {
|
||||||
|
resolve_types_expression(path.base(), ctx, diagnostics);
|
||||||
|
let base_type_info = ctx.get_type_info_for_node(path.base().node_id());
|
||||||
|
match base_type_info {
|
||||||
|
// TypeInfo::ClassInstance => {} todo
|
||||||
|
TypeInfo::__Error => {
|
||||||
|
// bubble it up
|
||||||
|
ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let message = format!(
|
||||||
|
"Incompatible type: cannot access a field on {}",
|
||||||
|
base_type_info
|
||||||
|
);
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&message,
|
||||||
|
path.source_range().start(),
|
||||||
|
path.source_range().end(),
|
||||||
|
);
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
|
||||||
|
// bubble it up
|
||||||
|
ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
||||||
let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id());
|
let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id());
|
||||||
ctx.associate_node_to_type(identifier.node_id(), type_info_id);
|
ctx.associate_node_to_type(identifier.node_id(), type_info_id);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user