diff --git a/axc/src/main.rs b/axc/src/main.rs index 0d3a75b..28b1e97 100644 --- a/axc/src/main.rs +++ b/axc/src/main.rs @@ -5,7 +5,7 @@ use std::{error::Error, fmt::Display, fs::File, io::Write, process::exit, str::F use axc::{ ast2ir::ast2ir, backends, - parser::{parser, ParserError}, + parser::{parser, ParserError, ParserMeta}, typeck::typeck, }; use clap::Parser; @@ -254,7 +254,8 @@ fn main() { let args = Args::parse(); let source = std::fs::read_to_string(&args.source_file)?; - let ast = chumsky::Parser::parse(&parser(), source).map_err(ParserError)?; + let meta = ParserMeta::default(); + let ast = chumsky::Parser::parse(&parser(&meta), source).map_err(ParserError)?; typeck(&ast)?; let ir = ast2ir(ast); diff --git a/axc/src/parser.rs b/axc/src/parser.rs index b430a8d..cbbf331 100644 --- a/axc/src/parser.rs +++ b/axc/src/parser.rs @@ -3,7 +3,7 @@ use std::{error::Error, fmt::Display}; use chumsky::{ - prelude::{choice, empty, end, just, todo, Simple}, + prelude::{choice, end, just, todo, Simple}, text::{ident, keyword}, Parser, }; @@ -25,44 +25,228 @@ impl Display for ParserError { impl Error for ParserError {} +/// Information required to be able to parse AlexScript code, beyond the code itself. +pub struct ParserMeta { + // This struct is just a total hacky workaround for the fact that chumsky isn't capable of + // parsing a context-sensitive grammar. I don't intend on ever fixing this: the stage-1 compiler + // will have a hardcoded list of operators, and the stage-2 compiler will have fully + // overloadable custom operators. + /// The list of registered binary operators. + operators: Vec, +} + +/// Definition of an operator. +struct OperatorDef { + /// The string of symbols that goes between two terms to invoke this operator. + name: String, + + /// The precedence level; if X has lower precedence than Y, then a X b Y c is a X (b Y c); + /// otherwise, it is (a X b) Y c. + precedence: u32, + + /// The associativity; if this is Left, then a X b X c is (a X b) X c; for Right, it is a X (b X + /// c); for None, it is a syntax error. + assoc: Option, +} + +/// The possible associativity directions of an operator. +enum Associativity { + Left, + Right, +} + +impl Default for ParserMeta { + fn default() -> Self { + use Associativity::*; + Self { + // These are mostly stolen from Haskell. + operators: vec![ + OperatorDef { + // Exponentiation. + name: "^".to_string(), + precedence: 8, + assoc: Some(Right), + }, + OperatorDef { + name: "*".to_string(), + precedence: 7, + assoc: Some(Left), + }, + OperatorDef { + // Division, which always returns an exact result and does not round to an + // integer (unlike C etc.). + name: "/".to_string(), + precedence: 7, + assoc: Some(Left), + }, + OperatorDef { + // Modulo, defined as Euclidean remainder. + name: "%".to_string(), + precedence: 7, + assoc: Some(Left), + }, + OperatorDef { + name: "+".to_string(), + precedence: 6, + assoc: Some(Left), + }, + OperatorDef { + name: "-".to_string(), + precedence: 6, + assoc: Some(Left), + }, + OperatorDef { + // Append to head of list. This might get removed since it's inefficient, + // depending. + name: "::".to_string(), + precedence: 5, + assoc: Some(Right), + }, + OperatorDef { + // Append lists. + name: "++".to_string(), + precedence: 5, + assoc: Some(Right), + }, + OperatorDef { + name: "==".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + name: "!=".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + name: "<".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + name: "<=".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + name: ">".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + name: ">=".to_string(), + precedence: 4, + assoc: None, + }, + OperatorDef { + // Functor map. + name: "<$>".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Functor map to constant. + name: "<$".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Flipped `<$`. + name: "$>".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Sequential application of applicative actions. + name: "<*>".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Sequence applicative actions, discarding the left value. + name: "*>".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Sequence applicative actions, discarding the right value. + name: "<*".to_string(), + precedence: 4, + assoc: Some(Left), + }, + OperatorDef { + // Binary and boolean `and`. + name: "&".to_string(), + precedence: 3, + assoc: Some(Right), + }, + OperatorDef { + // Binary and boolean `or`. + name: "|".to_string(), + precedence: 2, + assoc: Some(Right), + }, + OperatorDef { + // Monad sequence. + name: ">>".to_string(), + precedence: 1, + assoc: Some(Left), + }, + OperatorDef { + // Monad bind. + name: ">>=".to_string(), + precedence: 1, + assoc: Some(Left), + }, + OperatorDef { + // Function application. + name: "$".to_string(), + precedence: 1, + assoc: Some(Left), + }, + ], + } + } +} + /// Parser for AlexScript code. -pub fn parser() -> impl Parser> { - parse_statement() +pub fn parser(m: &ParserMeta) -> impl Parser> { + parse_statement(m) .repeated() .map(SyntaxTree) .then_ignore(end()) } -fn parse_statement() -> impl Parser> { +fn parse_statement(m: &ParserMeta) -> impl Parser> { choice(( - parse_type_def(), - parse_instance_def(), - parse_class_def(), - parse_class_decl_stmt(), + parse_type_def(m), + parse_instance_def(m), + parse_class_def(m), + parse_class_decl_stmt(m), )) } -fn parse_type_def() -> impl Parser> { +fn parse_type_def(m: &ParserMeta) -> impl Parser> { keyword("data") - .ignore_then(parse_type()) + .ignore_then(parse_type(m)) .then_ignore(just('=')) - .then(parse_constructor().repeated()) + .then(parse_constructor(m).repeated()) .then_ignore(just(';')) .map(|(typ, constructors)| Statement::TypeDefinition { typ, constructors }) } -fn parse_constructor() -> impl Parser> { +fn parse_constructor(m: &ParserMeta) -> impl Parser> { ident() - .then(parse_type().repeated()) + .then(parse_type(m).repeated()) .map(|(name, args)| TypeConstructor { name, args }) } -fn parse_instance_def() -> impl Parser> { +fn parse_instance_def(m: &ParserMeta) -> impl Parser> { keyword("instance") .ignore_then(ident()) - .then(parse_type()) + .then(parse_type(m)) .then( - parse_class_member() + parse_class_member(m) .repeated() .delimited_by(just('{'), just('}')), ) @@ -73,22 +257,19 @@ fn parse_instance_def() -> impl Parser> { }) } -fn parse_class_decl_stmt() -> impl Parser> { - parse_class_member().map(Statement::ClassMember) +fn parse_class_decl_stmt(m: &ParserMeta) -> impl Parser> { + parse_class_member(m).map(Statement::ClassMember) } -fn parse_class_member() -> impl Parser> { - choice((parse_func_decl(), parse_type_alias())) +fn parse_class_member(m: &ParserMeta) -> impl Parser> { + choice((parse_func_decl(m), parse_type_alias(m))) } -fn parse_func_decl() -> impl Parser> { +fn parse_func_decl(m: &ParserMeta) -> impl Parser> { keyword("def") .ignore_then(ident()) - .then(parse_pattern().repeated()) - .then(choice(( - just('=').ignore_then(parse_expression()).map(Some), - empty().to(None), - ))) + .then(parse_pattern(m).repeated()) + .then(just('=').ignore_then(parse_expression(m)).or_not()) .then_ignore(just(';')) .map(|((name, arguments), definition)| ClassMember::Function { name, @@ -97,37 +278,34 @@ fn parse_func_decl() -> impl Parser> { }) } -fn parse_type_alias() -> impl Parser> { +fn parse_type_alias(m: &ParserMeta) -> impl Parser> { keyword("type") - .ignore_then(parse_type()) - .then(choice(( - just('=').ignore_then(parse_type()).map(Some), - empty().to(None), - ))) + .ignore_then(parse_type(m)) + .then(just('=').ignore_then(parse_type(m)).or_not()) .then_ignore(just(';')) .map(|(left, right)| ClassMember::TypeAlias { left, right }) } -fn parse_class_def() -> impl Parser> { +fn parse_class_def(m: &ParserMeta) -> impl Parser> { keyword("class") .ignore_then(ident()) .then(ident()) .then( - parse_class_member() + parse_class_member(m) .repeated() .delimited_by(just('{'), just('}')), ) .map(|((name, var), decls)| Statement::ClassDefinition { name, var, decls }) } -fn parse_expression() -> impl Parser> { +fn parse_expression(m: &ParserMeta) -> impl Parser> { todo() } -fn parse_type() -> impl Parser> { +fn parse_type(m: &ParserMeta) -> impl Parser> { todo() } -fn parse_pattern() -> impl Parser> { +fn parse_pattern(m: &ParserMeta) -> impl Parser> { todo() }