Skip to content

Commit

Permalink
Started working on the new ocean lexer/parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmeaster30 committed Nov 12, 2023
1 parent 97da487 commit c7eeda1
Show file tree
Hide file tree
Showing 27 changed files with 715 additions and 108 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ It would be cool if we can have this language compile to several targets such as
- Java bytecode

### Static Analysis
I would like to be able to have the compiler understand what values certain variables can be at any given point in the program so we can check for things like unhandled cases, dead code, and out-of-bounds accesses at compile time. There are many more things I would want to check for but I don't know what I don't know.
I would like to be able to have the compiler understand what values certain variables can be at any given point in the program so we can check for things like unhandled cases, dead code, and out-of-bounds accesses at compile time.
There are many more things I would want to check for but I don't know what I don't know.
### Neat Generic System
I want to have generics determined by the operators and functions that are being used on the generic variables. This would provide some pretty neat functionality I feel but I haven't gotten to that point yet to really play with it.
I want to have generics determined by the operators and functions that are being used on the generic variables.
This would provide some pretty neat functionality I feel but I haven't gotten to that point yet to really play with it.
### Generic Operator Overloading
I don't think I would be able to implement this but I would like to add in the ability to override any symbol and provide the user with the ability to add their own operators and define the precedence and associativity of those operators. I tried writing out the parse for this and it proved to be really difficult so I am not sure if I will be able to get to this. Some things I noted was (especially with the type system I wanted (see Neat Generics System)) it would require a parser pass without parsing the expressions and then a type checking pass and then another parser pass to parse the expressions. Also, dealing with parsing conflicts when the user could potentially add an operator using the same symbol but the operator works on different types and have a different precedence order. Maybe this will be a different project.

My idea for this would involve parsing occurring in multiple steps with some type checking to figure out what functions exist and what annotations are on those functions.
In that type checking pass, we can figure out what operators we are adding and at what precedence level.
At this point, we can figure out if there are any issues with the declared operators.
The operator's function can have generic types because we are just generating the operator precedence table based on number of arguments and info in the annotation solely. The type info comes in the last type checking pass when we have already converted those operator expression nodes into function call nodes.
Then we can probably work out the macros here as well but I think that would be better done after the expression parsing pass in case there are any code replacements needed.
Then the next parsing pass will parse all the expression statements in the ast from the first pass. Since we will be walking the ast, we will have both recursive operators and scoped overloaded operators.
2 changes: 1 addition & 1 deletion examples/hydro/fibonacci.h2o
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module main

function fibonacci any body
function fibonacci u128 body
duplicate
duplicate
push u128 1
Expand Down
6 changes: 3 additions & 3 deletions examples/ocean/arrays.sea
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ let array: i32[] = 1..7

println(array)

let double = func (a: auto T) -> (result: T = 2 * a)
function double (a: auto T) -> (result: T = 2 * a)

# create array [2, 4, 6, 8, 10, 12, 14]
let doubled = array >. double

println(doubled)

let sum = func (input: (auto T)[]) -> (result: T):{
function sum (input: (auto T)[]) -> (result: T) {
result = 0
for val in input {
result += val
Expand All @@ -19,7 +19,7 @@ let sum = func (input: (auto T)[]) -> (result: T):{

let total = array.sum()

let mod2 = func (a: auto T) -> (result: T):{
function mod2 (a: auto T) -> (result: T) {
result = a % 2
}

Expand Down
6 changes: 3 additions & 3 deletions examples/ocean/collatz.sea
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
let isEven = func (x: i64) -> (result: bool = x % 2 == 0)
function isEven (x: i64) -> (result: bool = x % 2 == 0)

let isEmpty = func (x: string) -> (result: bool = x == "")
function isEmpty (x: string) -> (result: bool = x.length)

let collatz = func (input: i64) -> (path: i64[]):{
function collatz (input: i64) -> (path: i64[]) {
while input != 1 {
path.append(input)
if !input.isEven() {
Expand Down
2 changes: 1 addition & 1 deletion examples/ocean/errors/test.sea
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let hello = func() -> ():{
function hello() -> () {
while true {
continue
}
Expand Down
19 changes: 11 additions & 8 deletions examples/ocean/fibonacci.sea
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
let fib = func (input: i64) -> (result: i64):{
if input == 0 {
result = 0
using std.annotation.memoize
using std.annotation.trace

@Trace
@Memoize
function fib (input: i64) -> (result: i64) {
if input <= 1 {
result = input
} else {
result = input + self(input - 1)
result = fib(input - 1) + fib(input - 2)
}
}

#println(9.fib)
#println(fib(10))

let result = fib(10)
println(9.fib)
println(fib(10))
6 changes: 3 additions & 3 deletions examples/ocean/functions.sea
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std.io as io

let morph =
func (value: auto T, callback: func(T:T))
function (value: auto T, callback: function (T) -> T)
->
(result: T = callback(value))

let swap = func (a: auto T, b: T) -> (c: T = b, d: T = a)
function swap (a: auto T, b: T) -> (c: T = b, d: T = a)

let sum = func (a: i32, b: i32) -> (result: i32):
let sum = func (a: i32, b: i32) -> (result: i32)
{
result = a + b
}
2 changes: 1 addition & 1 deletion examples/ocean/hello_world.sea
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
use std.io

println()
println("hello world")
13 changes: 11 additions & 2 deletions examples/ocean/op_overloading.sea
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@
# Really not sure about the creating new arrays or the appending to arrays
# will need to think about that more

op _>._ func (arr: auto T[], f: func(T, T : T)) -> (result: T):{
result = []
@Operator {arr} >. {f}
function map(arr: auto T[], f: function(T, T) -> T) -> (result: T[]) {
for val in arr {
result.push(val.f())
}
}

@Operator {cond} ? {left} : {right}
function ternary(cond: bool, left: lazy auto T, right: lazy T) -> (result: T) {
if cond {
result = left
} else {
result = right
}
}
18 changes: 7 additions & 11 deletions examples/ocean/rational.sea
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let gcd = func (a: i64, b: i64) -> (result: i64):{
function gcd (a: i64, b: i64) -> (result: i64) {
# If A=0 then GCD(A, B)=B since the Greatest Common Divisor of 0 and B is B.
# If B=0 then GCD(A, B)=A since the Greatest Common Divisor of 0 and A is A.
# Let R be the remainder of dividing A by B assuming A > B. (R = A % B)
Expand All @@ -18,33 +18,29 @@ let gcd = func (a: i64, b: i64) -> (result: i64):{
}
}

let abs = func (a: i64) -> (result: i64):{
function abs (a: i64) -> (result: i64) {
if a < 0 {
result = -a
} else {
result = a
}
}

let lcm = func (a: i64, b: i64) -> (result: i64 = abs(a) * abs(b) / gcd(a, b))

#/
op + = (x: auto T, y: T) -> (result: T = add(x, y))

op - = (x: auto T, y: T) -> (result: T = subtract(x, y))
/#
function lcm (a: i64, b: i64) -> (result: i64 = abs(a) * abs(b) / gcd(a, b))

pack rational {
numerator: i64
denominator: i64
}

let add = func (a: rational, b: rational) -> (res: rational):{
@Operator {a} + {b}
function add (a: rational, b: rational) -> (res: rational) {
let mul: i64 = lcm(a.denominator, b.denominator)
res = ( a.numerator * mul + b.numerator * mul, mul )
}

let subtract = func (a: rational, b: rational) -> (res: rational):{
@Operator {a} - {b}
function subtract (a: rational, b: rational) -> (res: rational) {
let mul: i64 = lcm(a.denominator, b.denominator)
res = ( a.numerator * mul - b.numerator * mul, mul )
}
2 changes: 1 addition & 1 deletion src/hydro/frontend.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//TODO mod binaryable;
pub mod compiler;
pub mod parser;
pub mod token;
pub mod tokentype;
24 changes: 12 additions & 12 deletions src/hydro/frontend/parser.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use crate::hydro::frontend::token::{Token, TokenType};
use crate::hydro::frontend::tokentype::TokenType;
use crate::hydro::function::{Function, Target};
use crate::hydro::instruction::*;
use crate::hydro::intrinsic::Intrinsic;
use crate::hydro::layouttemplate::LayoutTemplate;
use crate::hydro::module::Module;
use crate::hydro::value::{Array, FunctionPointer, LayoutIndexRef, Reference, Type, Value, VariableRef};
use crate::util::tokentrait::TokenTrait;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::Read;
use crate::util::token::{Token, TokenTrait};

pub struct Parser {
file_contents: Vec<char>,
current_token: Option<Token>,
current_token: Option<Token<TokenType>>,
current_index: usize,
current_line: usize,
current_column: usize,
Expand Down Expand Up @@ -215,7 +215,7 @@ impl Parser {
function.add_label(target_name_token.lexeme, function.body.len());
}
TokenType::Module | TokenType::Function | TokenType::Layout | TokenType::Using | TokenType::Main | TokenType::Intrinsic => break,
_ => panic!("Expected to have an instruction here but read {:?} :(", inst_token),
_ => panic!("Expected to have an instruction here but read {} :(", inst_token),
}
}

Expand Down Expand Up @@ -347,7 +347,7 @@ impl Parser {
function: function_token.lexeme,
})
}
_ => panic!("Expected to have a value token here :( {:?}", value_token),
_ => panic!("Expected to have a value token here :( {}", value_token),
},
})
}
Expand Down Expand Up @@ -589,7 +589,7 @@ impl Parser {
!self.is_not_done()
}

fn token(&mut self) -> Option<Token> {
fn token(&mut self) -> Option<Token<TokenType>> {
match &self.current_token {
Some(current_token) => Some(current_token.clone()),
None => {
Expand Down Expand Up @@ -816,27 +816,27 @@ impl Parser {
lexeme.chars().all(|c| c.is_alphanumeric() || c == '.' || c == '-' || c == '_' || c == '\\' || c == '/')
}

fn expect_token(&mut self) -> Token {
fn expect_token(&mut self) -> Token<TokenType> {
match self.token() {
Some(token) => token,
None => panic!("Expected a token here!"),
}
}

fn expect_token_type(&mut self, token_type: TokenType) -> Token {
fn expect_token_type(&mut self, token_type: TokenType) -> Token<TokenType> {
match self.token() {
Some(token) => {
if token.is_token_type(token_type) {
token
} else {
panic!("Expected token type {:?} but got {:?}", token_type, token);
panic!("Expected token type {:?} but got {}", token_type, token);
}
}
None => panic!("Expected some token here :("),
}
}

fn optional_token_type(&mut self, token_type: TokenType) -> Option<Token> {
fn optional_token_type(&mut self, token_type: TokenType) -> Option<Token<TokenType>> {
match self.token() {
Some(token) => {
if token.is_token_type(token_type) {
Expand All @@ -849,13 +849,13 @@ impl Parser {
}
}

fn expect_one_of(&mut self, token_types: Vec<TokenType>) -> Token {
fn expect_one_of(&mut self, token_types: Vec<TokenType>) -> Token<TokenType> {
match self.token() {
Some(token) => {
if token_types.contains(&token.token_type) {
token
} else {
panic!("Expected one of {:?} but got {:?}", token_types, token);
panic!("Expected one of {:?} but got {}", token_types, token);
}
}
None => panic!("Expected some token here :("),
Expand Down
27 changes: 0 additions & 27 deletions src/hydro/frontend/token.rs → src/hydro/frontend/tokentype.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::util::tokentrait::TokenTrait;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TokenType {
Error,
Expand Down Expand Up @@ -64,28 +62,3 @@ pub enum TokenType {
True,
False,
}

#[derive(Clone, Debug)]
pub struct Token {
pub lexeme: String,
pub token_type: TokenType,
pub offset: (usize, usize),
pub line: (usize, usize),
pub column: (usize, usize),
}

impl TokenTrait<TokenType> for Token {
fn is_token_type(&self, value: TokenType) -> bool {
self.token_type == value
}

fn is_lexeme(&self, value: &str) -> bool {
self.lexeme == value
}
}

impl Token {
pub fn new(lexeme: String, token_type: TokenType, offset: (usize, usize), line: (usize, usize), column: (usize, usize)) -> Self {
Self { lexeme, token_type, offset, line, column }
}
}
1 change: 0 additions & 1 deletion src/hydro/intrinsic/intrinsicmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::hydro::executioncontext::ExecutionContext;
use crate::hydro::value::Value;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::io::Write;

Expand Down
10 changes: 10 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod hydro;
#[cfg(test)]
mod tests;
pub mod util;
mod ocean;

use crate::hydro::debugcontext::DebugContext;
use crate::hydro::frontend::compiler::HydroTranslateType;
Expand All @@ -10,6 +11,7 @@ use crate::hydro::Hydro;
use crate::util::argsparser::Command;
use std::env;
use util::argsparser::{ArgsParser, Argument};
use crate::ocean::Ocean;

fn main() -> std::io::Result<()> {
let args: Vec<String> = env::args().collect();
Expand All @@ -23,6 +25,11 @@ fn main() -> std::io::Result<()> {
.description("Print this help message"))
.command(Command::new("version")
.description("Print version information"))
.command(Command::new("run")
.arg(Argument::new("Source File")
.last()
.default("main.sea")
.help("The main source file to compile")))
.command(Command::new("hydro-build")
.arg(Argument::new("Output File")
.default("main.h2o.bin")
Expand Down Expand Up @@ -101,6 +108,9 @@ fn main() -> std::io::Result<()> {
Err(e) => e.print_stacktrace(),
}
}
"run" => {
Ocean::compile(arguments.get("Source File").unwrap().as_str()).unwrap();
}
_ => todo!("Unimplemented command :("),
},
None => {
Expand Down
3 changes: 3 additions & 0 deletions src/ocean.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod frontend;

pub struct Ocean {}
5 changes: 5 additions & 0 deletions src/ocean/frontend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod lexer;
pub mod tokentype;
pub mod compiler;
mod parserphase1;
mod ast;
Loading

0 comments on commit c7eeda1

Please sign in to comment.