Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Fuel HLL #2

Merged
merged 71 commits into from
May 8, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
159e747
initial parsing work
Jan 25, 2021
32d3ae5
parse trait declarations
Jan 27, 2021
42a8e80
import statements
Jan 27, 2021
c85792c
clean lexer tree; support method calls
Jan 27, 2021
9e71dda
basic ast skeleton beginnings
Jan 28, 2021
7a4edec
begin ast work
Jan 29, 2021
f767f96
checkpoint while i swap computers
Jan 29, 2021
40eb817
more ast parsing
Jan 29, 2021
8b7f12a
more ast parsing
Jan 29, 2021
e7bdaeb
ast: function appl, func decl, var exp
Jan 31, 2021
80ae5a9
ast: literals
Jan 31, 2021
73c4438
add compiler binary with nice error messages
Feb 1, 2021
d58e28b
ast: return statements, use statements
Feb 1, 2021
d40b2f2
fix error message
Feb 1, 2021
2e347c2
ast: traits
Feb 1, 2021
2a9edf9
more friendly parse error messages
Feb 1, 2021
d02fc85
old email
Feb 1, 2021
afdeef7
semicolons!
Feb 2, 2021
29ae513
byte literals; fix tests
Feb 3, 2021
5f8a909
parse binary and hex literals for byte and bytes32
Feb 4, 2021
9da8276
refactor expression parsing
Feb 4, 2021
8073cea
parse array exprs
Feb 4, 2021
6ddcb49
parse operator expressions
Feb 4, 2021
88e9fa5
type ascriptions
Feb 9, 2021
303f826
parser: polymorphism and trait bounds
Feb 9, 2021
f084b97
default to u64 instead of u32
Feb 10, 2021
720abfa
begin work on match statements
Feb 11, 2021
d23420c
parse structs
Feb 12, 2021
66b641c
refactor
Feb 13, 2021
3f2e581
a few program examples; parse match statements
Feb 13, 2021
f9234a6
lex struct expressions
Feb 16, 2021
4b2b948
parse to AST struct expressions
Feb 16, 2021
9e69615
compile warnings!
Feb 16, 2021
13e6a30
assert_or_warn macro
Feb 16, 2021
2af1365
generic struct parameters
Feb 17, 2021
98b46fa
parse contract/script/predicate
Feb 18, 2021
a11bf95
improve top level failure error message
Feb 18, 2021
937267e
s/ast/parse_tree
Feb 20, 2021
2f389a3
enums
Feb 20, 2021
0747988
begin semantic analysis
Feb 20, 2021
fc38750
begin work on type checking, inferencing, and semantics checking (#11)
sezna Feb 26, 2021
3b028e6
small error msg tweak
Feb 26, 2021
f7261d0
Refactor error and warning handling (#12)
sezna Mar 2, 2021
952ff98
type check predicates
Mar 3, 2021
029ea46
limit number of script main functions
Mar 3, 2021
217aa36
parse generic types for traits
Mar 4, 2021
3bed768
grammar for reassignments and while loops
Mar 4, 2021
ffa2450
variable reassignments; while loops
Mar 4, 2021
3eacf66
check generic type params in function declarations
Mar 4, 2021
6956d71
fix tests
Mar 5, 2021
d31ead1
grammar for inline asm
Mar 5, 2021
d74420a
Asm expressions (#13)
sezna Mar 28, 2021
ae30683
fix only last expr having type annotation; if branch enforcement of h…
Mar 30, 2021
a20104f
Control flow analysis + more (#28)
sezna Apr 16, 2021
12a0efa
add line_col method for errors and warnings
May 3, 2021
c7c73a9
Generate Assembly (#31)
sezna May 6, 2021
0a8a601
move license; clean warnings
May 6, 2021
e494d0c
Update forc/src/cli/build.rs
sezna May 6, 2021
09f3107
Update forc/src/cli/build.rs
sezna May 6, 2021
2d47def
code review feedback
May 6, 2021
f9a5a57
Merge branch 'parser_v1' of github.com:fuellabs/fuel-vm-hll into pars…
May 6, 2021
61c5b6e
Update forc/src/manifest.rs
sezna May 6, 2021
6b7b5b3
Update parser/src/control_flow_analysis/analyze_return_paths.rs
sezna May 6, 2021
824f0fd
more PR feedback
May 6, 2021
0e2793b
Merge branch 'parser_v1' of github.com:fuellabs/fuel-vm-hll into pars…
May 6, 2021
9328fcd
rename parser to core_lang
May 6, 2021
0127a8d
add minimum supported version
May 7, 2021
ee149cd
Update README.md
sezna May 7, 2021
a7416ce
check that struct size in words fits in a u32 gracefully
May 7, 2021
cfdfbcb
Merge branch 'parser_v1' of github.com:fuellabs/fuel-vm-hll into pars…
May 8, 2021
f1e8ff5
forgotten import
May 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
**/*/target/
target

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Expand Down
10 changes: 2 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
[package]
name = "hll"
version = "0.1.0"
authors = ["Alex <alex.hansen@fuel.sh>"]
edition = "2018"
[workspace]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
members = ["parser", "hllc"]
14 changes: 14 additions & 0 deletions hllc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "hllc"
version = "0.1.0"
authors = ["Alex Hansen <alex@fuel.sh>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
parser = { path = "../parser" }
structopt = "0.3"
source-span = "2.2"
termcolor = "1.1"
line-col = "0.2"
89 changes: 89 additions & 0 deletions hllc/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#![allow(warnings)]
use line_col::LineColLookup;
use parser::parse;
use source_span::{
fmt::{Color, Formatter, Style},
Position, SourceBuffer, Span, DEFAULT_METRICS,
};
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, Write};
use std::path::PathBuf;
use structopt::StructOpt;
use termcolor::{BufferWriter, Color as TermColor, ColorChoice, ColorSpec, WriteColor};

#[derive(Debug, StructOpt)]
#[structopt(name = "example", about = "An example of StructOpt usage.")]
struct Opt {
/// Input file
#[structopt(parse(from_os_str))]
input: PathBuf,

/// Output file, stdout if not present
#[structopt(short = "o", parse(from_os_str))]
output: Option<PathBuf>,
}

fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
let opt = Opt::from_args();
let content = fs::read_to_string(opt.input)?;

let res = parse(&content);

match res {
Ok(output) => {
if let Some(output) = opt.output {
let mut file = File::create(output)?;
file.write_all(content.as_bytes())?;
} else {
println!("{:#?}", output);
}
}
Err(e) => format_err(&content, e),
}

Ok(())
}

fn format_err(input: &str, err: parser::CompileError) {
let metrics = DEFAULT_METRICS;
let chars = input.chars().map(|x| -> Result<_, ()> { Ok(x) });

let metrics = source_span::DEFAULT_METRICS;
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);

let mut fmt = Formatter::with_margin_color(Color::Blue);

for c in buffer.iter() {
let c = c.unwrap(); // report eventual errors.
}

let (start_pos, end_pos) = err.span();
let lookup = LineColLookup::new(input);
let (start_line, start_col) = lookup.get(start_pos);
let (end_line, end_col) = lookup.get(end_pos - 1);

let err_start = Position::new(start_line - 1, start_col - 1);
let err_end = Position::new(end_line - 1, end_col - 1);
let err_span = Span::new(err_start, err_end, err_end.next_column());
fmt.add(err_span, Some(err.to_friendly_error_string()), Style::Error);

let formatted = fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap();
fmt.add(
buffer.span(),
Some("this is the whole program\nwhat a nice program!".to_string()),
Style::Error,
);

println!("{}", formatted);
write_red("Aborting due to previous error.").unwrap();
}

fn write_red(txt: &str) -> io::Result<()> {
let bufwtr = BufferWriter::stderr(ColorChoice::Always);
let mut buffer = bufwtr.buffer();
buffer.set_color(ColorSpec::new().set_fg(Some(TermColor::Red)))?;
writeln!(&mut buffer, "{}", txt)?;
bufwtr.print(&buffer)
}
13 changes: 13 additions & 0 deletions parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "parser"
version = "0.1.0"
authors = ["Alex <alex.hansen@fuel.sh>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
pest = "2.0"
pest_derive = "2.0"
thiserror = "1.0"
either = "1.6"
File renamed without changes.
File renamed without changes.
150 changes: 150 additions & 0 deletions parser/src/ast/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use crate::ast::Literal;
use crate::error::CompileError;
use crate::parser::{HllParser, Rule};
use either::Either;
use pest::iterators::Pair;

#[derive(Debug, Clone)]
pub(crate) enum Expression<'sc> {
Literal(Literal<'sc>),
FunctionApplication {
name: &'sc str,
arguments: Vec<Expression<'sc>>,
},
VariableExpression {
name: &'sc str,
},
Unit,
Array {
contents: Vec<Expression<'sc>>,
},
}

impl<'sc> Expression<'sc> {
pub(crate) fn parse_from_pair(expr: Pair<'sc, Rule>) -> Result<Self, CompileError<'sc>> {
let expr_for_debug = expr.clone();
let mut expr_iter = expr.into_inner();
// first expr is always here
let first_expr = expr_iter.next().unwrap();
let first_expr = Expression::parse_from_pair_inner(first_expr)?;
let mut expr_or_op_buf: Vec<Either<Op, Expression>> =
vec![Either::Right(first_expr.clone())];
// sometimes exprs are followed by ops in the same expr
while let Some(op) = expr_iter.next() {
let op_str = op.as_str();
let op = parse_op(op)?;
// an op is necessarily followed by an expression
let next_expr = match expr_iter.next() {
Some(o) => Expression::parse_from_pair_inner(o)?,
None => {
return Err(CompileError::ExpectedExprAfterOp {
op: op_str,
span: expr_for_debug.as_span(),
})
}
};
// pushing these into a vec in this manner so we can re-associate according to order of
// operations later
expr_or_op_buf.push(Either::Left(op));
expr_or_op_buf.push(Either::Right(next_expr));
/*
* TODO
* strategy: keep parsing until we have all of the op expressions
* re-associate the expr tree with operator precedence
*/
}
if expr_or_op_buf.len() == 1 {
Ok(first_expr)
} else {
eprintln!("Haven't yet implemented operator precedence");
Err(CompileError::Unimplemented(
Rule::op,
expr_for_debug.into_span(),
))
}
}

pub(crate) fn parse_from_pair_inner(expr: Pair<'sc, Rule>) -> Result<Self, CompileError<'sc>> {
let parsed = match expr.as_rule() {
Rule::literal_value => Expression::Literal(Literal::parse_from_pair(expr)?),
Rule::func_app => {
let mut func_app_parts = expr.into_inner();
let name = func_app_parts.next().unwrap().as_str();
let arguments = func_app_parts.next();
let arguments = arguments.map(|x| {
x.into_inner()
.map(|x| Expression::parse_from_pair_inner(x))
.collect::<Result<Vec<_>, _>>()
});
let arguments = arguments.unwrap_or_else(|| Ok(Vec::new()))?;

Expression::FunctionApplication { name, arguments }
}
Rule::var_exp => {
let mut var_exp_parts = expr.into_inner();
Expression::VariableExpression {
name: var_exp_parts.next().unwrap().as_str(),
}
}
Rule::array_exp => {
let mut array_exps = expr.into_inner();
Expression::Array {
contents: array_exps
.into_iter()
.map(|expr| Expression::parse_from_pair(expr))
.collect::<Result<_, _>>()?,
}
}
a => {
eprintln!(
"Unimplemented expr: {:?} ({:?}) ({:?})",
a,
expr.as_str(),
expr.as_span()
);
return Err(CompileError::Unimplemented(a, expr.as_span()));
}
};
Ok(parsed)
}
}

fn parse_op<'sc>(op: Pair<'sc, Rule>) -> Result<Op, CompileError<'sc>> {
use Op::*;
Ok(match op.as_str() {
"+" => Add,
"-" => Subtract,
"/" => Divide,
"*" => Multiply,
"%" => Modulo,
"||" => Or,
"&&" => And,
"==" => Equals,
"!=" => NotEquals,
"^" => Xor,
"|" => BinaryOr,
"&" => BinaryAnd,
a => {
return Err(CompileError::ExpectedOp {
op: a,
span: op.as_span(),
})
}
})
}

#[derive(Debug)]
enum Op {
Add,
Subtract,
Divide,
Multiply,
Modulo,
Or,
And,
Equals,
NotEquals,
Xor,
BinaryOr,
BinaryAnd,
}
100 changes: 100 additions & 0 deletions parser/src/ast/function_declaration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::ast::Expression;
use crate::{CodeBlock, CompileError, Rule};
use either::Either;
use pest::iterators::Pair;

#[derive(Debug)]
pub(crate) struct FunctionDeclaration<'sc> {
pub(crate) name: &'sc str,
pub(crate) body: CodeBlock<'sc>,
pub(crate) parameters: Vec<FunctionParameter<'sc>>,
pub(crate) span: pest::Span<'sc>,
pub(crate) return_type: TypeInfo<'sc>,
}

impl<'sc> FunctionDeclaration<'sc> {
pub(crate) fn parse_from_pair(pair: Pair<'sc, Rule>) -> Result<Self, CompileError<'sc>> {
let mut parts = pair.clone().into_inner();
let mut signature = parts.next().unwrap().into_inner();
let _fn_keyword = signature.next().unwrap();
let name = signature.next().unwrap().as_str();
let parameters = signature.next().unwrap();
let parameters = FunctionParameter::list_from_pairs(parameters.into_inner())?;
let return_type_signal = signature.next();
let return_type = match return_type_signal {
Some(_) => TypeInfo::parse_from_pair(signature.next().unwrap())?,
None => TypeInfo::Unit,
};
let body = parts.next().unwrap();
let body = CodeBlock::parse_from_pair(body)?;
Ok(FunctionDeclaration {
name,
parameters,
body,
span: pair.as_span(),
return_type,
})
}
}

#[derive(Debug)]
pub(crate) struct FunctionParameter<'sc> {
name: &'sc str,
r#type: TypeInfo<'sc>,
}

impl<'sc> FunctionParameter<'sc> {
pub(crate) fn list_from_pairs(
pairs: impl Iterator<Item = Pair<'sc, Rule>>,
) -> Result<Vec<FunctionParameter<'sc>>, CompileError<'sc>> {
pairs
.map(|pair: Pair<'sc, Rule>| {
let mut parts = pair.clone().into_inner();
let name = parts.next().unwrap().as_str();
let r#type = if name == "self" {
TypeInfo::SelfType
} else {
TypeInfo::parse_from_pair_inner(parts.next().unwrap())?
};
Ok(FunctionParameter { name, r#type })
})
.collect()
}
}

/// Type information without an associated value, used for type inferencing and definition.
#[derive(Debug)]
pub(crate) enum TypeInfo<'sc> {
String,
UnsignedInteger(IntegerBits),
Boolean,
Generic { name: &'sc str },
Unit,
SelfType,
}
#[derive(Debug)]
pub(crate) enum IntegerBits {
Eight,
Sixteen,
ThirtyTwo,
SixtyFour,
}

impl<'sc> TypeInfo<'sc> {
pub(crate) fn parse_from_pair(input: Pair<'sc, Rule>) -> Result<Self, CompileError<'sc>> {
let mut r#type = input.into_inner();
Self::parse_from_pair_inner(r#type.next().unwrap())
}
pub(crate) fn parse_from_pair_inner(input: Pair<'sc, Rule>) -> Result<Self, CompileError<'sc>> {
Ok(match input.as_str() {
"u8" => TypeInfo::UnsignedInteger(IntegerBits::Eight),
"u16" => TypeInfo::UnsignedInteger(IntegerBits::Sixteen),
"u32" => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo),
"u64" => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour),
"bool" => TypeInfo::Boolean,
"string" => TypeInfo::String,
"unit" => TypeInfo::Unit,
other => TypeInfo::Generic { name: other },
})
}
}
Loading