Most base expressions work.

This commit is contained in:
2025-09-26 09:24:56 -07:00
parent e9fb4fcd0f
commit 4362d82034
6 changed files with 1449 additions and 373 deletions

View File

@@ -1,46 +1,17 @@
mod error;
mod location;
mod name;
mod parse;
#[cfg(test)]
mod parser_tests;
pub mod tokens;
#[cfg(test)]
use crate::syntax::error::ParserError;
use crate::syntax::parse::Parser;
#[cfg(test)]
use crate::syntax::tokens::Lexer;
use codespan_reporting::diagnostic::Label;
pub use location::{Located, Location};
pub use name::Name;
use proptest_derive::Arbitrary;
use std::cmp::{max, min};
use std::fmt::Debug;
use std::ops::Range;
#[derive(Debug)]
pub struct Location {
file_id: usize,
span: Range<usize>,
}
impl Location {
pub fn new(file_id: usize, span: Range<usize>) -> Self {
Location { file_id, span }
}
pub fn extend_to(&self, other: &Location) -> Location {
assert_eq!(self.file_id, other.file_id);
Location {
file_id: self.file_id,
span: min(self.span.start, other.span.start)..max(self.span.end, other.span.end),
}
}
pub fn primary_label(&self) -> Label<usize> {
Label::primary(self.file_id, self.span.clone())
}
pub fn secondary_label(&self) -> Label<usize> {
Label::secondary(self.file_id, self.span.clone())
}
}
#[derive(Debug)]
pub struct Module {
definitions: Vec<Definition>,
@@ -54,6 +25,12 @@ pub struct Definition {
definition: Def,
}
impl Located for Definition {
fn location(&self) -> Location {
self.location.clone()
}
}
#[derive(Debug)]
pub enum Def {
Enumeration(EnumerationDef),
@@ -62,28 +39,29 @@ pub enum Def {
Value(ValueDef),
}
impl Def {
fn location(&self) -> &Location {
impl Located for Def {
fn location(&self) -> Location {
match self {
Def::Enumeration(def) => &def.location,
Def::Structure(def) => &def.location,
Def::Function(def) => &def.location,
Def::Value(def) => &def.location,
Def::Enumeration(def) => def.location.clone(),
Def::Structure(def) => def.location.clone(),
Def::Function(def) => def.location.clone(),
Def::Value(def) => def.location.clone(),
}
}
}
#[derive(Debug)]
pub struct EnumerationDef {
name: String,
location: Location,
options: Vec<EnumerationVariant>,
variants: Vec<EnumerationVariant>,
}
#[derive(Debug)]
pub struct EnumerationVariant {
location: Location,
name: String,
arguments: Vec<Type>,
argument: Option<Type>,
}
#[derive(Debug)]
@@ -95,8 +73,10 @@ pub struct StructureDef {
#[derive(Debug)]
pub struct StructureField {
location: Location,
export: ExportClass,
name: String,
field_type: Type,
field_type: Option<Type>,
}
#[derive(Debug)]
@@ -118,7 +98,7 @@ pub struct FunctionArg {
pub struct ValueDef {
name: String,
location: Location,
value: Value,
value: Expression,
}
#[derive(Debug)]
@@ -142,7 +122,16 @@ pub struct BindingStmt {
#[derive(Debug)]
pub enum Expression {
Value(Value),
Value(ConstantValue),
Reference(Name),
EnumerationValue(Name, Name, Option<Box<Expression>>),
StructureValue(Name, Vec<FieldValue>),
}
#[derive(Debug)]
pub struct FieldValue {
field: Name,
value: Expression,
}
#[derive(Debug)]
@@ -160,9 +149,8 @@ impl TypeRestrictions {
#[derive(Debug)]
pub struct TypeRestriction {
location: Location,
class: String,
variables: Vec<String>,
constructor: Type,
arguments: Vec<Type>,
}
#[derive(Debug)]
@@ -174,9 +162,28 @@ pub enum Type {
Function(Vec<Type>, Box<Type>),
}
#[derive(Debug)]
pub enum Value {
Constant(ConstantValue),
impl Located for Type {
fn location(&self) -> Location {
match self {
Type::Constructor(l, _) => l.clone(),
Type::Variable(l, _) => l.clone(),
Type::Primitive(l, _) => l.clone(),
Type::Application(t1, ts) => {
let mut result = t1.location();
if let Some(last) = ts.last() {
result = result.extend_to(&last.location());
}
result
}
Type::Function(args, ret) => {
if let Some(first) = args.first() {
first.location().extend_to(&ret.location())
} else {
ret.location()
}
}
}
}
}
#[derive(Debug)]
@@ -198,113 +205,3 @@ pub struct IntegerWithBase {
base: Option<u8>,
value: u64,
}
#[test]
fn can_parse_constants() {
let parse_constant = |str| {
let lexer = Lexer::from(str);
let mut result = Parser::new(0, lexer);
result.parse_constant()
};
assert!(matches!(
parse_constant("16"),
Ok(ConstantValue::Integer(
_,
IntegerWithBase {
base: None,
value: 16,
}
))
));
assert!(matches!(
parse_constant("0x10"),
Ok(ConstantValue::Integer(
_,
IntegerWithBase {
base: Some(16),
value: 16,
}
))
));
assert!(matches!(
parse_constant("0o20"),
Ok(ConstantValue::Integer(
_,
IntegerWithBase {
base: Some(8),
value: 16,
}
))
));
assert!(matches!(
parse_constant("0b10000"),
Ok(ConstantValue::Integer(
_,
IntegerWithBase {
base: Some(2),
value: 16,
}
))
));
assert!(
matches!(parse_constant("\"foo\""), Ok(ConstantValue::String(_, x))
if x == "foo")
);
assert!(matches!(
parse_constant("'f'"),
Ok(ConstantValue::Character(_, 'f'))
));
}
#[test]
fn can_parse_types() {
let parse_type = |str| {
let lexer = Lexer::from(str);
let mut result = Parser::new(0, lexer);
result.parse_type()
};
assert!(matches!(
parse_type("Cons"),
Ok(Type::Application(cons, empty)) if
matches!(cons.as_ref(), Type::Constructor(_, c) if c == "Cons") &&
empty.is_empty()
));
assert!(matches!(
parse_type("cons"),
Ok(Type::Variable(_, c)) if c == "cons"
));
assert!(matches!(
parse_type("Cons a b"),
Ok(Type::Application(a, b))
if matches!(a.as_ref(), Type::Constructor(_, c) if c == "Cons") &&
matches!(b.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)]
if b1 == "a" && b2 == "b")
));
println!("------");
println!("result: {:?}", parse_type("a -> z"));
println!("------");
assert!(matches!(
parse_type("a -> z"),
Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Variable(_, a1)] if a1 == "a") &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z")
));
assert!(matches!(
parse_type("a b -> z"),
Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Variable(_, a1), Type::Variable(_, b1)]
if a1 == "a" && b1 == "b") &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z")
));
assert!(matches!(
parse_type("Cons a b -> z"),
Ok(Type::Function(a, z))
if matches!(a.as_slice(), [Type::Application(cons, appargs)]
if matches!(cons.as_ref(), Type::Constructor(_, c) if c == "Cons") &&
matches!(appargs.as_slice(), [Type::Variable(_, b1), Type::Variable(_, b2)]
if b1 == "a" && b2 == "b")) &&
matches!(z.as_ref(), Type::Variable(_, z1) if z1 == "z")
));
}