Rudimentary type stuff
This commit is contained in:
parent
555d2a7ba5
commit
1d9e5edfba
@ -278,37 +278,43 @@ op := '+', '-', etc.
|
|||||||
|
|
||||||
|
|
||||||
/* Schala EBNF Grammar */
|
/* Schala EBNF Grammar */
|
||||||
/*
|
/* Terminal productions are «in Guillemets» or UPPERCASE if they are a class
|
||||||
|
* or not representable in ASCII
|
||||||
|
|
||||||
program := (statement delimiter)* EOF
|
program := (statement delimiter)* EOF
|
||||||
delimiter := NEWLINE | SEMICOLON
|
delimiter := NEWLINE | «;»
|
||||||
statement := expression | declaration
|
statement := expression | declaration
|
||||||
|
|
||||||
declaration := type_declaration | func_declaration
|
declaration := type_alias | type_declaration | func_declaration
|
||||||
|
|
||||||
type_declaration := TYPE IDENTIFIER
|
type_alias := «alias» IDENTIFIER «=» IDENTIFIER
|
||||||
func_declaration := FN IDENTIFIER LParen param_list RParen
|
type_declaration := «type» IDENTIFIER «=» type_body
|
||||||
|
type_body := variant_specifier («|» variant_specifier)*
|
||||||
|
variant_specifier := «{» member_list «}»
|
||||||
|
member_list := (IDENTIFIER type_anno)*
|
||||||
|
|
||||||
param_list := (IDENTIFIER type_anno+ Comma)*
|
func_declaration := «fn» IDENTIFIER «(» param_list «)»
|
||||||
|
param_list := (IDENTIFIER type_anno+ «,»)*
|
||||||
|
|
||||||
type_anno := Colon type
|
type_anno := «:» type
|
||||||
|
|
||||||
expression := precedence_expr
|
expression := precedence_expr
|
||||||
precedence_expr := primary
|
precedence_expr := primary
|
||||||
primary := literal | paren_expr | identifier_expr
|
primary := literal | paren_expr | identifier_expr
|
||||||
|
|
||||||
paren_expr := LParen expression RParen
|
paren_expr := LParen expression RParen
|
||||||
identifier_expr := call_expr | index_expr | IDENTIFIER
|
identifier_expr := call_expr | index_expr | IDENTIFIER
|
||||||
literal := TRUE | FALSE | number_literal | str_literal
|
literal := «true» | «false» | number_literal | str_literal
|
||||||
|
|
||||||
call_expr := IDENTIFIER LParen expr_list RParen //TODO maybe make this optional? or no, have a bare identifier meant to be used as method taken care of in eval
|
call_expr := IDENTIFIER «(» expr_list «)» //TODO maybe make this optional? or no, have a bare identifier meant to be used as method taken care of in eval
|
||||||
index_expr := LBracket (expression, Comma+)* RBracket
|
index_expr := «(» (expression («,» (expression)* | ε) «)»
|
||||||
expr_list := expression (Comma expression)* | ε
|
expr_list := expression («,» expression)* | ε
|
||||||
|
|
||||||
// a float_literal can still be assigned to an int in type-checking
|
// a float_literal can still be assigned to an int in type-checking
|
||||||
number_literal := int_literal | float_literal
|
number_literal := int_literal | float_literal
|
||||||
int_literal = (HEX_SIGIL | BIN_SIGIL) digits
|
int_literal = ('0x' | '0b') digits
|
||||||
float_literal := digits (PERIOD digits)
|
float_literal := digits ('.' digits)
|
||||||
digits := (digit_group underscore)+
|
digits := (DIGIT_GROUP underscore)+
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -351,6 +357,12 @@ macro_rules! expect {
|
|||||||
$token_type => $self.next(),
|
$token_type => $self.next(),
|
||||||
_ => return Err(ParseError { msg: $message.to_string() }),
|
_ => return Err(ParseError { msg: $message.to_string() }),
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
($self:expr, $token_type:pat if $cond:expr, $message:expr) => {
|
||||||
|
match $self.peek() {
|
||||||
|
$token_type if $cond => $self.next(),
|
||||||
|
_ => return Err(ParseError { msg: $message.to_string() }),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,12 +378,18 @@ pub enum Statement {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
FuncDecl,
|
FuncDecl,
|
||||||
TypeDecl(Rc<String>, TypeBody)
|
TypeDecl(Rc<String>, TypeBody),
|
||||||
|
TypeAlias(Rc<String>, Rc<String>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum TypeBody {
|
pub struct TypeBody(Vec<Variant>);
|
||||||
TypeBody
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Variant {
|
||||||
|
Singleton(Rc<String>),
|
||||||
|
//ArgumentConstructor,
|
||||||
|
//Record
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -425,16 +443,32 @@ impl Parser {
|
|||||||
fn statement(&mut self) -> ParseResult<Statement> {
|
fn statement(&mut self) -> ParseResult<Statement> {
|
||||||
//TODO handle error recovery here
|
//TODO handle error recovery here
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
|
Keyword(Alias) => self.type_alias().map(|alias| { Statement::Declaration(alias) }),
|
||||||
Keyword(Type) => self.type_declaration().map(|decl| { Statement::Declaration(decl) }),
|
Keyword(Type) => self.type_declaration().map(|decl| { Statement::Declaration(decl) }),
|
||||||
Keyword(Func)=> self.func_declaration().map(|func| { Statement::Declaration(func) }),
|
Keyword(Func)=> self.func_declaration().map(|func| { Statement::Declaration(func) }),
|
||||||
_ => self.expression().map(|expr| { Statement::Expression(expr) } ),
|
_ => self.expression().map(|expr| { Statement::Expression(expr) } ),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_alias(&mut self) -> ParseResult<Declaration> {
|
||||||
|
expect!(self, Keyword(Alias), "Expected 'alias'");
|
||||||
|
let alias = self.identifier()?;
|
||||||
|
expect!(self, Operator(ref c) if **c == "=", "Expected '='");
|
||||||
|
let original = self.identifier()?;
|
||||||
|
Ok(Declaration::TypeAlias(alias, original))
|
||||||
|
}
|
||||||
|
|
||||||
fn type_declaration(&mut self) -> ParseResult<Declaration> {
|
fn type_declaration(&mut self) -> ParseResult<Declaration> {
|
||||||
expect!(self, Keyword(Type), "Expected 'type'");
|
expect!(self, Keyword(Type), "Expected 'type'");
|
||||||
let name = self.identifier()?;
|
let name = self.identifier()?;
|
||||||
Ok(Declaration::TypeDecl(name, TypeBody::TypeBody))
|
expect!(self, Operator(ref c) if **c == "=", "Expected '='");
|
||||||
|
let body = self.type_body()?;
|
||||||
|
Ok(Declaration::TypeDecl(name, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_body(&mut self) -> ParseResult<TypeBody> {
|
||||||
|
let variant = Variant::Singleton(self.identifier()?);
|
||||||
|
Ok(TypeBody(vec!(variant)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn func_declaration(&mut self) -> ParseResult<Declaration> {
|
fn func_declaration(&mut self) -> ParseResult<Declaration> {
|
||||||
@ -609,9 +643,14 @@ pub fn parse(input: Vec<Token>) -> Result<AST, ParseError> {
|
|||||||
mod parse_tests {
|
mod parse_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::Statement::*;
|
use super::Statement::*;
|
||||||
|
use super::Declaration::*;
|
||||||
use super::Expression::*;
|
use super::Expression::*;
|
||||||
use super::ParseError;
|
use super::ParseError;
|
||||||
|
|
||||||
|
macro_rules! rc {
|
||||||
|
($string:expr) => { Rc::new($string.to_string()) }
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! parse_test {
|
macro_rules! parse_test {
|
||||||
($string:expr, $correct:expr) => { assert_eq!(parse(tokenize($string)).unwrap(), $correct) }
|
($string:expr, $correct:expr) => { assert_eq!(parse(tokenize($string)).unwrap(), $correct) }
|
||||||
}
|
}
|
||||||
@ -665,4 +704,9 @@ mod parse_tests {
|
|||||||
parse_test!("a", AST(vec![Expression(var!("a"))]));
|
parse_test!("a", AST(vec![Expression(var!("a"))]));
|
||||||
parse_test!("a + b", AST(vec![Expression(binexp!(op!("+"), var!("a"), var!("b")))]));
|
parse_test!("a + b", AST(vec![Expression(binexp!(op!("+"), var!("a"), var!("b")))]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parsing_types() {
|
||||||
|
parse_test!("type Yolo = Yolo", AST(vec![Declaration(TypeDecl(rc!("Yolo"), TypeBody(vec![Variant::Singleton(rc!("Yolo"))])))]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user