feat: lexer
This commit is contained in:
parent
208f2e80ea
commit
1d6809661c
|
@ -59,6 +59,7 @@ dependencies = [
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"internment",
|
"internment",
|
||||||
"miette",
|
"miette",
|
||||||
|
"vec1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -885,6 +886,12 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec1"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fc1631c774f0f9570797191e01247cbefde789eebfbf128074cb934115a6133"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
|
@ -7,6 +7,15 @@ use clap::{Parser, Subcommand};
|
||||||
#[clap(version, about, long_about = None)]
|
#[clap(version, about, long_about = None)]
|
||||||
#[clap(propagate_version = true)]
|
#[clap(propagate_version = true)]
|
||||||
pub enum Args {
|
pub enum Args {
|
||||||
|
/// Build an aiken project
|
||||||
|
Build,
|
||||||
|
/// Start a development server
|
||||||
|
Dev,
|
||||||
|
/// Create a new aiken project
|
||||||
|
New {
|
||||||
|
/// Project name
|
||||||
|
name: PathBuf,
|
||||||
|
},
|
||||||
/// A subcommand for working with Untyped Plutus Core
|
/// A subcommand for working with Untyped Plutus Core
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Uplc(UplcCommand),
|
Uplc(UplcCommand),
|
||||||
|
@ -15,33 +24,49 @@ pub enum Args {
|
||||||
/// Commands for working with Untyped Plutus Core
|
/// Commands for working with Untyped Plutus Core
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum UplcCommand {
|
pub enum UplcCommand {
|
||||||
|
/// Evaluate an Untyped Plutus Core program
|
||||||
|
Eval {
|
||||||
|
/// Handle input as flat bytes
|
||||||
|
#[clap(short, long)]
|
||||||
|
flat: bool,
|
||||||
|
|
||||||
|
/// File to load and evaluate
|
||||||
|
input: PathBuf,
|
||||||
|
},
|
||||||
/// Encode textual Untyped Plutus Core to flat bytes
|
/// Encode textual Untyped Plutus Core to flat bytes
|
||||||
Flat {
|
Flat {
|
||||||
|
/// Textual Untyped Plutus Core file
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
#[clap(short, long)]
|
|
||||||
print: bool,
|
/// Output file name
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
out: Option<String>,
|
out: Option<String>,
|
||||||
},
|
|
||||||
/// Decode flat bytes to textual Untyped Plutus Core
|
/// Print output instead of saving to file
|
||||||
Unflat {
|
|
||||||
input: PathBuf,
|
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
print: bool,
|
print: bool,
|
||||||
#[clap(short, long)]
|
|
||||||
out: Option<String>,
|
|
||||||
},
|
},
|
||||||
/// Format an Untyped Plutus Core program
|
/// Format an Untyped Plutus Core program
|
||||||
Fmt {
|
Fmt {
|
||||||
|
/// Textual Untyped Plutus Core file
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
|
|
||||||
|
/// Print output instead of saving to file
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
print: bool,
|
print: bool,
|
||||||
},
|
},
|
||||||
/// Evaluate an Untyped Plutus Core program
|
/// Decode flat bytes to textual Untyped Plutus Core
|
||||||
Eval {
|
Unflat {
|
||||||
|
/// Flat encoded Untyped Plutus Core file
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
|
|
||||||
|
/// Output file name
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
flat: bool,
|
out: Option<String>,
|
||||||
|
|
||||||
|
/// Print output instead of saving to file
|
||||||
|
#[clap(short, long)]
|
||||||
|
print: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,31 @@ fn main() -> anyhow::Result<()> {
|
||||||
let args = Args::default();
|
let args = Args::default();
|
||||||
|
|
||||||
match args {
|
match args {
|
||||||
|
Args::Build => {
|
||||||
|
// 1. load and parse modules
|
||||||
|
// * lib - contains modules, types, and functions
|
||||||
|
// * contracts - contains validators
|
||||||
|
// * scripts - contains native scripts dsl
|
||||||
|
// 2. type check everything
|
||||||
|
// 3. generate uplc and policy/address if relevant
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
Args::Dev => {
|
||||||
|
// launch a development server
|
||||||
|
// this should allow people to test
|
||||||
|
// their contracts over http
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
Args::New { name } => {
|
||||||
|
if !name.exists() {
|
||||||
|
fs::create_dir_all(name.join("lib"))?;
|
||||||
|
fs::create_dir_all(name.join("policies"))?;
|
||||||
|
fs::create_dir_all(name.join("scripts"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Args::Uplc(uplc) => match uplc {
|
Args::Uplc(uplc) => match uplc {
|
||||||
UplcCommand::Flat { input, print, out } => {
|
UplcCommand::Flat { input, print, out } => {
|
||||||
let code = std::fs::read_to_string(&input)?;
|
let code = std::fs::read_to_string(&input)?;
|
||||||
|
@ -48,6 +73,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
fs::write(&out_name, &bytes)?;
|
fs::write(&out_name, &bytes)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UplcCommand::Fmt { input, print } => {
|
UplcCommand::Fmt { input, print } => {
|
||||||
let code = std::fs::read_to_string(&input)?;
|
let code = std::fs::read_to_string(&input)?;
|
||||||
|
|
||||||
|
@ -61,6 +87,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
fs::write(&input, pretty)?;
|
fs::write(&input, pretty)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UplcCommand::Unflat { input, print, out } => {
|
UplcCommand::Unflat { input, print, out } => {
|
||||||
let bytes = std::fs::read(&input)?;
|
let bytes = std::fs::read(&input)?;
|
||||||
|
|
||||||
|
@ -82,6 +109,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
fs::write(&out_name, pretty)?;
|
fs::write(&out_name, pretty)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UplcCommand::Eval { input, flat } => {
|
UplcCommand::Eval { input, flat } => {
|
||||||
let program = if flat {
|
let program = if flat {
|
||||||
let bytes = std::fs::read(&input)?;
|
let bytes = std::fs::read(&input)?;
|
||||||
|
|
|
@ -9,3 +9,4 @@ edition = "2021"
|
||||||
chumsky = "0.8.0"
|
chumsky = "0.8.0"
|
||||||
internment = "0.7.0"
|
internment = "0.7.0"
|
||||||
miette = "5.2.0"
|
miette = "5.2.0"
|
||||||
|
vec1 = "1.8.0"
|
||||||
|
|
|
@ -1,47 +1,505 @@
|
||||||
pub struct Module {
|
use std::{collections::HashMap, fmt, ops::Range, sync::Arc};
|
||||||
pub name: Vec<String>,
|
|
||||||
pub docs: Vec<String>,
|
use internment::Intern;
|
||||||
pub is_script: bool,
|
|
||||||
pub is_lib: bool,
|
use crate::{
|
||||||
pub is_policy: bool,
|
expr::{TypedExpr, UntypedExpr},
|
||||||
|
tipo::{self, PatternConstructor, Type, ValueConstructor},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type TypedModule = Module<tipo::Module, TypedDefinition>;
|
||||||
|
pub type UntypedModule = Module<(), UntypedDefinition>;
|
||||||
|
|
||||||
|
pub enum ModuleKind {
|
||||||
|
Contract,
|
||||||
|
Lib,
|
||||||
|
Script,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Definition {
|
pub struct Module<Info, Definitions> {
|
||||||
|
pub name: Vec<String>,
|
||||||
|
pub docs: Vec<String>,
|
||||||
|
pub type_info: Info,
|
||||||
|
pub definitons: Vec<Definitions>,
|
||||||
|
pub kind: ModuleKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
|
||||||
|
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
||||||
|
|
||||||
|
pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
Fn {
|
Fn {
|
||||||
arguments: Vec<String>,
|
location: Span,
|
||||||
|
arguments: Vec<Vec<Arg<T>>>,
|
||||||
body: Expr,
|
body: Expr,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
name: String,
|
name: String,
|
||||||
public: bool,
|
public: bool,
|
||||||
return_annotation: Option<()>,
|
return_annotation: Option<Annotation>,
|
||||||
return_type: (),
|
return_type: T,
|
||||||
},
|
},
|
||||||
|
|
||||||
TypeAlias {
|
TypeAlias {
|
||||||
|
location: Span,
|
||||||
alias: String,
|
alias: String,
|
||||||
annotation: (),
|
annotation: Annotation,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
parameters: Vec<String>,
|
parameters: Vec<String>,
|
||||||
public: bool,
|
public: bool,
|
||||||
tipo: (),
|
tipo: T,
|
||||||
},
|
},
|
||||||
|
|
||||||
DataType {
|
DataType {
|
||||||
constructors: Vec<()>,
|
location: Span,
|
||||||
|
constructors: Vec<RecordConstructor<T>>,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
name: String,
|
name: String,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
parameters: Vec<String>,
|
parameters: Vec<String>,
|
||||||
public: bool,
|
public: bool,
|
||||||
typed_parameters: Vec<()>,
|
typed_parameters: Vec<T>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Use {
|
Use {
|
||||||
module: Vec<String>,
|
module: Vec<String>,
|
||||||
as_name: Option<String>,
|
as_name: Option<String>,
|
||||||
// unqualified: Vec<UnqualifiedImport>,
|
unqualified: Vec<UnqualifiedImport>,
|
||||||
// package: PackageName,
|
package: PackageName,
|
||||||
|
},
|
||||||
|
|
||||||
|
ModuleConstant {
|
||||||
|
doc: Option<String>,
|
||||||
|
location: Span,
|
||||||
|
public: bool,
|
||||||
|
name: String,
|
||||||
|
annotation: Option<Annotation>,
|
||||||
|
value: Box<Constant<T, ConstantRecordTag>>,
|
||||||
|
tipo: T,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Expr {}
|
pub type TypedConstant = Constant<Arc<Type>, String>;
|
||||||
|
pub type UntypedConstant = Constant<(), ()>;
|
||||||
|
|
||||||
|
pub enum Constant<T, RecordTag> {
|
||||||
|
Int {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
String {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Pair {
|
||||||
|
location: Span,
|
||||||
|
elements: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
List {
|
||||||
|
location: Span,
|
||||||
|
elements: Vec<Self>,
|
||||||
|
tipo: T,
|
||||||
|
},
|
||||||
|
|
||||||
|
Record {
|
||||||
|
location: Span,
|
||||||
|
module: Option<String>,
|
||||||
|
name: String,
|
||||||
|
args: Vec<CallArg<Self>>,
|
||||||
|
tag: RecordTag,
|
||||||
|
tipo: T,
|
||||||
|
field_map: Option<FieldMap>,
|
||||||
|
},
|
||||||
|
|
||||||
|
ByteString {
|
||||||
|
location: Span,
|
||||||
|
// segments: Vec<BitStringSegment<Self, T>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
module: Option<String>,
|
||||||
|
name: String,
|
||||||
|
constructor: Option<Box<ValueConstructor>>,
|
||||||
|
tipo: T,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CallArg<A> {
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub location: Span,
|
||||||
|
pub value: A,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldMap {
|
||||||
|
pub arity: usize,
|
||||||
|
pub fields: HashMap<String, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecordConstructor<T> {
|
||||||
|
pub location: Span,
|
||||||
|
pub name: String,
|
||||||
|
pub arguments: Vec<RecordConstructorArg<T>>,
|
||||||
|
pub documentation: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecordConstructorArg<T> {
|
||||||
|
pub label: Option<String>,
|
||||||
|
// ast
|
||||||
|
pub annotation: Annotation,
|
||||||
|
pub location: Span,
|
||||||
|
pub tipo: T,
|
||||||
|
pub doc: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Arg<T> {
|
||||||
|
pub names: ArgName,
|
||||||
|
pub location: Span,
|
||||||
|
pub annotation: Option<Annotation>,
|
||||||
|
pub tipo: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ArgName {
|
||||||
|
Discard { name: String },
|
||||||
|
LabeledDiscard { label: String, name: String },
|
||||||
|
Named { name: String },
|
||||||
|
NamedLabeled { name: String, label: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UnqualifiedImport {
|
||||||
|
pub location: Span,
|
||||||
|
pub name: String,
|
||||||
|
pub as_name: Option<String>,
|
||||||
|
pub layer: Layer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeAst
|
||||||
|
pub enum Annotation {
|
||||||
|
Constructor {
|
||||||
|
location: Span,
|
||||||
|
module: Option<String>,
|
||||||
|
name: String,
|
||||||
|
arguments: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Fn {
|
||||||
|
location: Span,
|
||||||
|
arguments: Vec<Self>,
|
||||||
|
ret: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Tuple {
|
||||||
|
location: Span,
|
||||||
|
elems: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Hole {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Layer {
|
||||||
|
Value,
|
||||||
|
Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Layer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Layer::Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BinOp {
|
||||||
|
// Boolean logic
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
Eq,
|
||||||
|
NotEq,
|
||||||
|
|
||||||
|
// Order comparison
|
||||||
|
LtInt,
|
||||||
|
LtEqInt,
|
||||||
|
GtEqInt,
|
||||||
|
GtInt,
|
||||||
|
|
||||||
|
// Maths
|
||||||
|
AddInt,
|
||||||
|
SubInt,
|
||||||
|
MultInt,
|
||||||
|
DivInt,
|
||||||
|
ModInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Pattern<Constructor, Type> {
|
||||||
|
Int {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
String {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The creation of a variable.
|
||||||
|
/// e.g. `assert [this_is_a_var, .._] = x`
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A reference to a variable in a bit string. This is always a variable
|
||||||
|
/// being used rather than a new variable being assigned.
|
||||||
|
VarUsage {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
tipo: Type,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A name given to a sub-pattern using the `as` keyword.
|
||||||
|
/// e.g. `assert #(1, [_, _] as the_list) = x`
|
||||||
|
Assign {
|
||||||
|
name: String,
|
||||||
|
location: Span,
|
||||||
|
pattern: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A pattern that binds to any value but does not assign a variable.
|
||||||
|
/// Always starts with an underscore.
|
||||||
|
Discard {
|
||||||
|
name: String,
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
List {
|
||||||
|
location: Span,
|
||||||
|
elements: Vec<Self>,
|
||||||
|
tail: Option<Box<Self>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The constructor for a custom type. Starts with an uppercase letter.
|
||||||
|
Constructor {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
arguments: Vec<CallArg<Self>>,
|
||||||
|
module: Option<String>,
|
||||||
|
constructor: Constructor,
|
||||||
|
with_spread: bool,
|
||||||
|
tipo: Type,
|
||||||
|
},
|
||||||
|
|
||||||
|
Tuple {
|
||||||
|
location: Span,
|
||||||
|
elems: Vec<Self>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AssignmentKind {
|
||||||
|
Let,
|
||||||
|
Assert,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type MultiPattern<PatternConstructor, Type> = Vec<Pattern<PatternConstructor, Type>>;
|
||||||
|
|
||||||
|
pub type UntypedMultiPattern = MultiPattern<(), ()>;
|
||||||
|
pub type TypedMultiPattern = MultiPattern<PatternConstructor, Arc<Type>>;
|
||||||
|
|
||||||
|
pub type TypedClause = Clause<TypedExpr, PatternConstructor, Arc<Type>, String>;
|
||||||
|
|
||||||
|
pub type UntypedClause = Clause<UntypedExpr, (), (), ()>;
|
||||||
|
|
||||||
|
pub struct Clause<Expr, PatternConstructor, Type, RecordTag> {
|
||||||
|
pub location: Span,
|
||||||
|
pub pattern: MultiPattern<PatternConstructor, Type>,
|
||||||
|
pub alternative_patterns: Vec<MultiPattern<PatternConstructor, Type>>,
|
||||||
|
pub guard: Option<ClauseGuard<Type, RecordTag>>,
|
||||||
|
pub then: Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ClauseGuard<Type, RecordTag> {
|
||||||
|
Equals {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
NotEquals {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
GtInt {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
GtEqInt {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
LtInt {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
LtEqInt {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Or {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
And {
|
||||||
|
location: Span,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
tipo: Type,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
TupleIndex {
|
||||||
|
location: Span,
|
||||||
|
index: u64,
|
||||||
|
tipo: Type,
|
||||||
|
tuple: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Constant(Constant<Type, RecordTag>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypedRecordUpdateArg {
|
||||||
|
pub label: String,
|
||||||
|
pub location: Span,
|
||||||
|
pub value: TypedExpr,
|
||||||
|
pub index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UntypedRecordUpdateArg {
|
||||||
|
pub label: String,
|
||||||
|
// pub location: SrcSpan,
|
||||||
|
pub value: UntypedExpr,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecordUpdateSpread {
|
||||||
|
pub base: Box<UntypedExpr>,
|
||||||
|
pub location: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TodoKind {
|
||||||
|
Keyword,
|
||||||
|
EmptyFunction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct SrcId(Intern<Vec<String>>);
|
||||||
|
|
||||||
|
impl SrcId {
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
SrcId(Intern::new(Vec::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Span {
|
||||||
|
pub src: SrcId,
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Span {
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
use chumsky::Span;
|
||||||
|
|
||||||
|
Self::new(SrcId::empty(), 0..0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn src(&self) -> SrcId {
|
||||||
|
self.src
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> Range<usize> {
|
||||||
|
use chumsky::Span;
|
||||||
|
|
||||||
|
self.start()..self.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(self, other: Self) -> Self {
|
||||||
|
use chumsky::Span;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
self.src, other.src,
|
||||||
|
"attempted to union spans with different sources"
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
start: self.start().min(other.start()),
|
||||||
|
end: self.end().max(other.end()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Span {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}:{:?}", self.src, self.range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl chumsky::Span for Span {
|
||||||
|
type Context = SrcId;
|
||||||
|
|
||||||
|
type Offset = usize;
|
||||||
|
|
||||||
|
fn new(context: Self::Context, range: Range<Self::Offset>) -> Self {
|
||||||
|
assert!(range.start <= range.end);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
src: context,
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context(&self) -> Self::Context {
|
||||||
|
self.src
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&self) -> Self::Offset {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(&self) -> Self::Offset {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub enum Origin {
|
||||||
|
Src,
|
||||||
|
Test,
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
use std::{collections::HashSet, fmt};
|
||||||
|
|
||||||
|
use crate::{ast::Span, token::Token};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParseError {
|
||||||
|
kind: ErrorKind,
|
||||||
|
span: Span,
|
||||||
|
while_parsing: Option<(Span, &'static str)>,
|
||||||
|
expected: HashSet<Pattern>,
|
||||||
|
label: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseError {
|
||||||
|
pub fn merge(mut self, other: Self) -> Self {
|
||||||
|
// TODO: Use HashSet
|
||||||
|
for expected in other.expected.into_iter() {
|
||||||
|
self.expected.insert(expected);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ParseError {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.kind == other.kind && self.span == other.span && self.label == other.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Pattern>> chumsky::Error<T> for ParseError {
|
||||||
|
type Span = Span;
|
||||||
|
|
||||||
|
type Label = &'static str;
|
||||||
|
|
||||||
|
fn expected_input_found<Iter: IntoIterator<Item = Option<T>>>(
|
||||||
|
span: Self::Span,
|
||||||
|
expected: Iter,
|
||||||
|
found: Option<T>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: found
|
||||||
|
.map(Into::into)
|
||||||
|
.map(ErrorKind::Unexpected)
|
||||||
|
.unwrap_or(ErrorKind::UnexpectedEnd),
|
||||||
|
span,
|
||||||
|
while_parsing: None,
|
||||||
|
expected: expected
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.map(Into::into).unwrap_or(Pattern::End))
|
||||||
|
.collect(),
|
||||||
|
label: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_label(mut self, label: Self::Label) -> Self {
|
||||||
|
self.label.get_or_insert(label);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge(self, other: Self) -> Self {
|
||||||
|
ParseError::merge(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
UnexpectedEnd,
|
||||||
|
Unexpected(Pattern),
|
||||||
|
Unclosed {
|
||||||
|
start: Pattern,
|
||||||
|
before_span: Span,
|
||||||
|
before: Option<Pattern>,
|
||||||
|
},
|
||||||
|
NoEndBranch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Pattern {
|
||||||
|
Char(char),
|
||||||
|
Token(Token),
|
||||||
|
Literal,
|
||||||
|
TypeIdent,
|
||||||
|
TermIdent,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Pattern {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
Self::Char(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Token> for Pattern {
|
||||||
|
fn from(tok: Token) -> Self {
|
||||||
|
Self::Token(tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Pattern {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Pattern::Token(token) => write!(f, "{}", token),
|
||||||
|
Pattern::Char(c) => write!(f, "{:?}", c),
|
||||||
|
Pattern::Literal => write!(f, "literal"),
|
||||||
|
Pattern::TypeIdent => write!(f, "type name"),
|
||||||
|
Pattern::TermIdent => write!(f, "identifier"),
|
||||||
|
Pattern::End => write!(f, "end of input"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,269 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use vec1::Vec1;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{
|
||||||
|
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, Pattern, RecordUpdateSpread, Span,
|
||||||
|
TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg,
|
||||||
|
},
|
||||||
|
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum TypedExpr {
|
||||||
|
Int {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
String {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Sequence {
|
||||||
|
location: Span,
|
||||||
|
expressions: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A chain of pipe expressions.
|
||||||
|
/// By this point the type checker has expanded it into a series of
|
||||||
|
/// assignments and function calls, but we still have a Pipeline AST node as
|
||||||
|
/// even though it is identical to `Sequence` we want to use different
|
||||||
|
/// locations when showing it in error messages, etc.
|
||||||
|
Pipeline {
|
||||||
|
location: Span,
|
||||||
|
expressions: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
constructor: ValueConstructor,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Fn {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
is_capture: bool,
|
||||||
|
args: Vec<Arg<Arc<Type>>>,
|
||||||
|
body: Box<Self>,
|
||||||
|
return_annotation: Option<Annotation>,
|
||||||
|
},
|
||||||
|
|
||||||
|
List {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
elements: Vec<Self>,
|
||||||
|
tail: Option<Box<Self>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Call {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
fun: Box<Self>,
|
||||||
|
args: Vec<CallArg<Self>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
BinOp {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
name: BinOp,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Assignment {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
value: Box<Self>,
|
||||||
|
pattern: Pattern<PatternConstructor, Arc<Type>>,
|
||||||
|
kind: AssignmentKind,
|
||||||
|
},
|
||||||
|
|
||||||
|
Try {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
value: Box<Self>,
|
||||||
|
then: Box<Self>,
|
||||||
|
pattern: Pattern<PatternConstructor, Arc<Type>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
When {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
subjects: Vec<Self>,
|
||||||
|
clauses: Vec<Clause<Self, PatternConstructor, Arc<Type>, String>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RecordAccess {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
label: String,
|
||||||
|
index: u64,
|
||||||
|
record: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
ModuleSelect {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
label: String,
|
||||||
|
module_name: String,
|
||||||
|
module_alias: String,
|
||||||
|
constructor: ModuleValueConstructor,
|
||||||
|
},
|
||||||
|
|
||||||
|
Tuple {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
elems: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
TupleIndex {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
index: u64,
|
||||||
|
tuple: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Todo {
|
||||||
|
location: Span,
|
||||||
|
label: Option<String>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RecordUpdate {
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
spread: Box<Self>,
|
||||||
|
args: Vec<TypedRecordUpdateArg>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Negate {
|
||||||
|
location: Span,
|
||||||
|
value: Box<Self>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UntypedExpr {
|
||||||
|
Int {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
String {
|
||||||
|
location: Span,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Sequence {
|
||||||
|
location: Span,
|
||||||
|
expressions: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Var {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Fn {
|
||||||
|
location: Span,
|
||||||
|
is_capture: bool,
|
||||||
|
arguments: Vec<Arg<()>>,
|
||||||
|
body: Box<Self>,
|
||||||
|
return_annotation: Option<Annotation>,
|
||||||
|
},
|
||||||
|
|
||||||
|
List {
|
||||||
|
location: Span,
|
||||||
|
elements: Vec<Self>,
|
||||||
|
tail: Option<Box<Self>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Call {
|
||||||
|
location: Span,
|
||||||
|
fun: Box<Self>,
|
||||||
|
arguments: Vec<CallArg<Self>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
BinOp {
|
||||||
|
location: Span,
|
||||||
|
name: BinOp,
|
||||||
|
left: Box<Self>,
|
||||||
|
right: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
PipeLine {
|
||||||
|
expressions: Vec1<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Assignment {
|
||||||
|
location: Span,
|
||||||
|
value: Box<Self>,
|
||||||
|
pattern: Pattern<(), ()>,
|
||||||
|
kind: AssignmentKind,
|
||||||
|
annotation: Option<Annotation>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Try {
|
||||||
|
location: Span,
|
||||||
|
value: Box<Self>,
|
||||||
|
pattern: Pattern<(), ()>,
|
||||||
|
then: Box<Self>,
|
||||||
|
annotation: Option<Annotation>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Case {
|
||||||
|
location: Span,
|
||||||
|
subjects: Vec<Self>,
|
||||||
|
clauses: Vec<Clause<Self, (), (), ()>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
FieldAccess {
|
||||||
|
location: Span,
|
||||||
|
label: String,
|
||||||
|
container: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Tuple {
|
||||||
|
location: Span,
|
||||||
|
elems: Vec<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
TupleIndex {
|
||||||
|
location: Span,
|
||||||
|
index: u64,
|
||||||
|
tuple: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Todo {
|
||||||
|
kind: TodoKind,
|
||||||
|
location: Span,
|
||||||
|
label: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RecordUpdate {
|
||||||
|
location: Span,
|
||||||
|
constructor: Box<Self>,
|
||||||
|
spread: RecordUpdateSpread,
|
||||||
|
arguments: Vec<UntypedRecordUpdateArg>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Negate {
|
||||||
|
location: Span,
|
||||||
|
value: Box<Self>,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
use internment::Intern;
|
||||||
|
|
||||||
|
use crate::{ast::Span, error::ParseError, token::Token};
|
||||||
|
|
||||||
|
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
|
let int = text::int(10)
|
||||||
|
.map(Intern::new)
|
||||||
|
.map(|value| Token::Int { value });
|
||||||
|
|
||||||
|
let op = choice((
|
||||||
|
just("==").to(Token::EqualEqual),
|
||||||
|
just('=').to(Token::Equal),
|
||||||
|
just("..").to(Token::Dot),
|
||||||
|
just('.').to(Token::Dot),
|
||||||
|
just("!=").to(Token::NotEqual),
|
||||||
|
just('!').to(Token::Bang),
|
||||||
|
just("<=").to(Token::LessEqual),
|
||||||
|
just('<').to(Token::Less),
|
||||||
|
just(">=").to(Token::GreaterEqual),
|
||||||
|
just('>').to(Token::Greater),
|
||||||
|
just('+').to(Token::Plus),
|
||||||
|
just("->").to(Token::RArrow),
|
||||||
|
just('-').to(Token::Minus),
|
||||||
|
just('*').to(Token::Star),
|
||||||
|
just('/').to(Token::Slash),
|
||||||
|
just('%').to(Token::Percent),
|
||||||
|
just("|>").to(Token::Pipe),
|
||||||
|
));
|
||||||
|
|
||||||
|
let grouping = choice((
|
||||||
|
just('(').to(Token::LeftParen),
|
||||||
|
just(')').to(Token::RightParen),
|
||||||
|
just('[').to(Token::LeftSquare),
|
||||||
|
just(']').to(Token::RightSquare),
|
||||||
|
just('{').to(Token::LeftBrace),
|
||||||
|
just('}').to(Token::RightBrace),
|
||||||
|
));
|
||||||
|
|
||||||
|
let escape = just('\\').ignore_then(
|
||||||
|
just('\\')
|
||||||
|
.or(just('/'))
|
||||||
|
.or(just('"'))
|
||||||
|
.or(just('b').to('\x08'))
|
||||||
|
.or(just('f').to('\x0C'))
|
||||||
|
.or(just('n').to('\n'))
|
||||||
|
.or(just('r').to('\r'))
|
||||||
|
.or(just('t').to('\t')),
|
||||||
|
);
|
||||||
|
|
||||||
|
let string = just('"')
|
||||||
|
.ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated())
|
||||||
|
.then_ignore(just('"'))
|
||||||
|
.collect::<String>()
|
||||||
|
.map(Intern::new)
|
||||||
|
.map(|value| Token::String { value })
|
||||||
|
.labelled("string");
|
||||||
|
|
||||||
|
let keyword = text::ident().map(|s: String| match s.as_str() {
|
||||||
|
"as" => Token::As,
|
||||||
|
"assert" => Token::Assert,
|
||||||
|
"const" => Token::Const,
|
||||||
|
"fn" => Token::Fn,
|
||||||
|
"if" => Token::If,
|
||||||
|
"is" => Token::Is,
|
||||||
|
"let" => Token::Let,
|
||||||
|
"opaque" => Token::Opaque,
|
||||||
|
"pub" => Token::Pub,
|
||||||
|
"use" => Token::Use,
|
||||||
|
"todo" => Token::Todo,
|
||||||
|
"try" => Token::Try,
|
||||||
|
"type" => Token::Type,
|
||||||
|
"when" => Token::When,
|
||||||
|
_ => {
|
||||||
|
if s.chars().next().map_or(false, |c| c.is_uppercase()) {
|
||||||
|
Token::UpName {
|
||||||
|
// TODO: do not allow _ in upname
|
||||||
|
name: Intern::new(s),
|
||||||
|
}
|
||||||
|
} else if s.starts_with('_') {
|
||||||
|
Token::DiscardName {
|
||||||
|
// TODO: do not allow uppercase letters in discard name
|
||||||
|
name: Intern::new(s),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Token::Name {
|
||||||
|
// TODO: do not allow uppercase letters in name
|
||||||
|
name: Intern::new(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let token = choice((keyword, int, op, grouping, string))
|
||||||
|
.or(any().map(Token::Error).validate(|t, span, emit| {
|
||||||
|
emit(ParseError::expected_input_found(span, None, Some(t)));
|
||||||
|
t
|
||||||
|
}))
|
||||||
|
.map_with_span(move |token, span| (token, span))
|
||||||
|
.padded()
|
||||||
|
.recover_with(skip_then_retry_until([]));
|
||||||
|
|
||||||
|
let comments = just("//")
|
||||||
|
.then_ignore(
|
||||||
|
just('(')
|
||||||
|
.ignore_then(take_until(just(")#")).ignored())
|
||||||
|
.or(none_of('\n').ignored().repeated().ignored()),
|
||||||
|
)
|
||||||
|
.padded()
|
||||||
|
.ignored()
|
||||||
|
.repeated();
|
||||||
|
|
||||||
|
token
|
||||||
|
.padded_by(comments)
|
||||||
|
.repeated()
|
||||||
|
.padded()
|
||||||
|
.then_ignore(end())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
use internment::Intern;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{Span, SrcId},
|
||||||
|
lexer,
|
||||||
|
token::Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple() {
|
||||||
|
let code = "pub type |> >=\n{ Thing _na_thing name";
|
||||||
|
let len = code.chars().count();
|
||||||
|
|
||||||
|
let span = |i| Span::new(SrcId::empty(), i..i + 1);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lexer::lexer()
|
||||||
|
.parse(chumsky::Stream::from_iter(
|
||||||
|
span(len),
|
||||||
|
code.chars().enumerate().map(|(i, c)| (c, span(i))),
|
||||||
|
))
|
||||||
|
.map(|tokens| tokens.into_iter().map(|(tok, _)| tok).collect::<Vec<_>>()),
|
||||||
|
Ok(vec![
|
||||||
|
Token::Pub,
|
||||||
|
Token::Type,
|
||||||
|
Token::Pipe,
|
||||||
|
Token::GreaterEqual,
|
||||||
|
Token::LeftBrace,
|
||||||
|
Token::UpName {
|
||||||
|
name: Intern::new("Thing".to_string())
|
||||||
|
},
|
||||||
|
Token::DiscardName {
|
||||||
|
name: Intern::new("_na_thing".to_string())
|
||||||
|
},
|
||||||
|
Token::Name {
|
||||||
|
name: Intern::new("name".to_string())
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
pub mod build;
|
||||||
#[cfg(test)]
|
pub mod error;
|
||||||
mod tests {
|
pub mod expr;
|
||||||
#[test]
|
pub mod lexer;
|
||||||
fn it_works() {
|
pub mod parser;
|
||||||
let result = 2 + 2;
|
pub mod tipo;
|
||||||
assert_eq!(result, 4);
|
pub mod token;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use crate::{ast, error::ParseError, token::Token};
|
||||||
|
|
||||||
|
pub fn module_parser() -> impl Parser<Token, ast::UntypedModule, Error = ParseError> {
|
||||||
|
let imports = just(Token::Use).ignore_then();
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{Constant, FieldMap, Span, TypedConstant},
|
||||||
|
build::Origin,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum Type {
|
||||||
|
/// A nominal (named) type such as `Int`, `Float`, or a programmer defined
|
||||||
|
/// custom type such as `Person`. The type can take other types as
|
||||||
|
/// arguments (aka "generics" or "parametric polymorphism").
|
||||||
|
///
|
||||||
|
/// If the type is defined in the Gleam prelude the `module` field will be
|
||||||
|
/// empty, otherwise it will contain the name of the module that
|
||||||
|
/// defines the type.
|
||||||
|
///
|
||||||
|
App {
|
||||||
|
public: bool,
|
||||||
|
module: Vec<String>,
|
||||||
|
name: String,
|
||||||
|
args: Vec<Arc<Type>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The type of a function. It takes arguments and returns a value.
|
||||||
|
///
|
||||||
|
Fn {
|
||||||
|
args: Vec<Arc<Type>>,
|
||||||
|
retrn: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A type variable. See the contained `TypeVar` enum for more information.
|
||||||
|
///
|
||||||
|
Var { tipo: Arc<RefCell<TypeVar>> },
|
||||||
|
|
||||||
|
/// A tuple is an ordered collection of 0 or more values, each of which
|
||||||
|
/// can have a different type, so the `tuple` type is the sum of all the
|
||||||
|
/// contained types.
|
||||||
|
///
|
||||||
|
Tuple { elems: Vec<Arc<Type>> },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TypeVar {
|
||||||
|
/// Unbound is an unbound variable. It is one specific type but we don't
|
||||||
|
/// know what yet in the inference process. It has a unique id which can be used to
|
||||||
|
/// identify if two unbound variable Rust values are the same Gleam type variable
|
||||||
|
/// instance or not.
|
||||||
|
///
|
||||||
|
Unbound { id: u64 },
|
||||||
|
/// Link is type variable where it was an unbound variable but we worked out
|
||||||
|
/// that it is some other type and now we point to that one.
|
||||||
|
///
|
||||||
|
Link { tipo: Arc<Type> },
|
||||||
|
/// A Generic variable stands in for any possible type and cannot be
|
||||||
|
/// specialised to any one type
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```gleam
|
||||||
|
/// type Cat(a) {
|
||||||
|
/// Cat(name: a)
|
||||||
|
/// }
|
||||||
|
/// // a is TypeVar::Generic
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
Generic { id: u64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ValueConstructor {
|
||||||
|
pub public: bool,
|
||||||
|
pub variant: ValueConstructorVariant,
|
||||||
|
pub tipo: Arc<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ValueConstructorVariant {
|
||||||
|
/// A locally defined variable or function parameter
|
||||||
|
LocalVariable { location: Span },
|
||||||
|
|
||||||
|
/// A module constant
|
||||||
|
ModuleConstant {
|
||||||
|
location: Span,
|
||||||
|
module: String,
|
||||||
|
literal: Constant<Arc<Type>, String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A function belonging to the module
|
||||||
|
ModuleFn {
|
||||||
|
name: String,
|
||||||
|
field_map: Option<FieldMap>,
|
||||||
|
module: Vec<String>,
|
||||||
|
arity: usize,
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A constructor for a custom type
|
||||||
|
Record {
|
||||||
|
name: String,
|
||||||
|
arity: usize,
|
||||||
|
field_map: Option<FieldMap>,
|
||||||
|
location: Span,
|
||||||
|
module: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Module {
|
||||||
|
pub name: Vec<String>,
|
||||||
|
pub origin: Origin,
|
||||||
|
pub package: String,
|
||||||
|
pub types: HashMap<String, TypeConstructor>,
|
||||||
|
pub types_constructors: HashMap<String, Vec<String>>,
|
||||||
|
pub values: HashMap<String, ValueConstructor>,
|
||||||
|
pub accessors: HashMap<String, AccessorsMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypeConstructor {
|
||||||
|
pub public: bool,
|
||||||
|
pub origin: Span,
|
||||||
|
pub module: Vec<String>,
|
||||||
|
pub parameters: Vec<Arc<Type>>,
|
||||||
|
pub typ: Arc<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AccessorsMap {
|
||||||
|
pub public: bool,
|
||||||
|
pub tipo: Arc<Type>,
|
||||||
|
pub accessors: HashMap<String, RecordAccessor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecordAccessor {
|
||||||
|
// TODO: smaller int. Doesn't need to be this big
|
||||||
|
pub index: u64,
|
||||||
|
pub label: String,
|
||||||
|
pub tipo: Arc<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PatternConstructor {
|
||||||
|
Record {
|
||||||
|
name: String,
|
||||||
|
field_map: Option<FieldMap>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ModuleValueConstructor {
|
||||||
|
Record {
|
||||||
|
name: String,
|
||||||
|
arity: usize,
|
||||||
|
type_: Arc<Type>,
|
||||||
|
field_map: Option<FieldMap>,
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
Fn {
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
Constant {
|
||||||
|
literal: TypedConstant,
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use internment::Intern;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
|
||||||
|
pub enum Token {
|
||||||
|
Error(char),
|
||||||
|
Name { name: Intern<String> },
|
||||||
|
UpName { name: Intern<String> },
|
||||||
|
DiscardName { name: Intern<String> },
|
||||||
|
Int { value: Intern<String> },
|
||||||
|
String { value: Intern<String> },
|
||||||
|
// Groupings
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftSquare, // [
|
||||||
|
RightSquare, // }
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
// Int Operators
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Less,
|
||||||
|
Greater,
|
||||||
|
LessEqual,
|
||||||
|
GreaterEqual,
|
||||||
|
Percent,
|
||||||
|
// ByteString Operators
|
||||||
|
PlusDot, // '+.'
|
||||||
|
MinusDot, // '-.'
|
||||||
|
StarDot, // '*.'
|
||||||
|
SlashDot, // '/.'
|
||||||
|
LessDot, // '<.'
|
||||||
|
GreaterDot, // '>.'
|
||||||
|
LessEqualDot, // '<=.'
|
||||||
|
GreaterEqualDot, // '>=.'
|
||||||
|
// Other Punctuation
|
||||||
|
Colon,
|
||||||
|
Comma,
|
||||||
|
Hash, // '#'
|
||||||
|
Bang, // '!'
|
||||||
|
Equal,
|
||||||
|
EqualEqual, // '=='
|
||||||
|
NotEqual, // '!='
|
||||||
|
Vbar, // '|'
|
||||||
|
VbarVbar, // '||'
|
||||||
|
AmperAmper, // '&&'
|
||||||
|
Pipe, // '|>'
|
||||||
|
Dot, // '.'
|
||||||
|
RArrow, // '->'
|
||||||
|
DotDot, // '..'
|
||||||
|
EndOfFile,
|
||||||
|
// Extra
|
||||||
|
CommentNormal,
|
||||||
|
CommentDoc,
|
||||||
|
CommentModule,
|
||||||
|
EmptyLine,
|
||||||
|
// Keywords (alphabetically):
|
||||||
|
As,
|
||||||
|
Assert,
|
||||||
|
Const,
|
||||||
|
Fn,
|
||||||
|
If,
|
||||||
|
Is,
|
||||||
|
Let,
|
||||||
|
Opaque,
|
||||||
|
Pub,
|
||||||
|
Use,
|
||||||
|
Todo,
|
||||||
|
Try,
|
||||||
|
Type,
|
||||||
|
When,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Token {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
Token::Error(c) => {
|
||||||
|
write!(f, "\"{}\"", c)?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Token::Name { name } => &**name,
|
||||||
|
Token::UpName { name } => &**name,
|
||||||
|
Token::DiscardName { name } => &**name,
|
||||||
|
Token::Int { value } => &**value,
|
||||||
|
Token::String { value } => &**value,
|
||||||
|
Token::LeftParen => "(",
|
||||||
|
Token::RightParen => ")",
|
||||||
|
Token::LeftSquare => "[",
|
||||||
|
Token::RightSquare => "]",
|
||||||
|
Token::LeftBrace => "{",
|
||||||
|
Token::RightBrace => "}",
|
||||||
|
Token::Plus => "+",
|
||||||
|
Token::Minus => "-",
|
||||||
|
Token::Star => "*",
|
||||||
|
Token::Slash => "/",
|
||||||
|
Token::Less => "<",
|
||||||
|
Token::Greater => ">",
|
||||||
|
Token::LessEqual => "<=",
|
||||||
|
Token::GreaterEqual => ">=",
|
||||||
|
Token::Percent => "%",
|
||||||
|
Token::PlusDot => "+.",
|
||||||
|
Token::MinusDot => "-.",
|
||||||
|
Token::StarDot => "*.",
|
||||||
|
Token::SlashDot => "/.",
|
||||||
|
Token::LessDot => "<.",
|
||||||
|
Token::GreaterDot => ">.",
|
||||||
|
Token::LessEqualDot => "<=.",
|
||||||
|
Token::GreaterEqualDot => ">=.",
|
||||||
|
Token::Colon => ":",
|
||||||
|
Token::Comma => ",",
|
||||||
|
Token::Hash => "#",
|
||||||
|
Token::Bang => "!",
|
||||||
|
Token::Equal => "=",
|
||||||
|
Token::EqualEqual => "==",
|
||||||
|
Token::NotEqual => "!=",
|
||||||
|
Token::Vbar => "|",
|
||||||
|
Token::VbarVbar => "||",
|
||||||
|
Token::AmperAmper => "&&",
|
||||||
|
Token::Pipe => "|>",
|
||||||
|
Token::Dot => ".",
|
||||||
|
Token::RArrow => "->",
|
||||||
|
Token::DotDot => "..",
|
||||||
|
Token::EndOfFile => "EOF",
|
||||||
|
Token::CommentNormal => "//",
|
||||||
|
Token::CommentDoc => "///",
|
||||||
|
Token::CommentModule => "////",
|
||||||
|
Token::EmptyLine => "EMPTYLINE",
|
||||||
|
Token::As => "as",
|
||||||
|
Token::Assert => "assert",
|
||||||
|
Token::When => "when",
|
||||||
|
Token::Is => "is",
|
||||||
|
Token::Const => "const",
|
||||||
|
Token::Fn => "fn",
|
||||||
|
Token::If => "if",
|
||||||
|
Token::Use => "import",
|
||||||
|
Token::Let => "let",
|
||||||
|
Token::Opaque => "opaque",
|
||||||
|
Token::Pub => "pub",
|
||||||
|
Token::Todo => "todo",
|
||||||
|
Token::Try => "try",
|
||||||
|
Token::Type => "type",
|
||||||
|
};
|
||||||
|
write!(f, "\"{}\"", s)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue