Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
150 changes: 78 additions & 72 deletions MILESTONES.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# Milestones

## v0.1 - initially tagged version
- [ ] Language versioning
## v0.2
- [ ] Test suite
- [ ] JSON module

## v0.2 - initial pre-release
## v0.3 - initial pre-release
- [ ] Migrate AST to use actual Ast module
- [ ] Regular expressions
- [ ] Test suite
- [ ] Regular expressions (libpcre?)
- [ ] Constraint system
- language version
- permissions
- [ ] JSON module
- [ ] Allow non-zero exit codes on sub-processes
- [ ] Bug: stacktrace printing is wrong in REPL

## v1.0
- [ ] Formatter
Expand All @@ -25,6 +23,7 @@

## Stretch goals
- [ ] Bytecode VM (this should not happen until API becomes stable)
- [ ] From scratch regular expression engine
- [ ] Unicode strings
- [ ] Method syntax for hash maps
- [ ] Pattern matching
Expand All @@ -37,72 +36,79 @@

# Done

## v0.1 - initially tagged version
- [x] Language versioning

## pre-v0.1
- [x] Lexical scoping
- [x] Closures
- [x] Variable re-assignment
- [x] Function arguments
- [x] Invoking function expressions
- [x] Conditionals
- [x] Tooling to diagnose shift/reduce conflicts
- [x] Infix functions
- [x] Recursion (depends on conditionals)
- [x] Bug: $scriptDir and $cwd should be absolute, so they can be chained
- [x] `List` methods
- `.pop(element)`
- `.push(element)`
- `.reduce(callback)`
- `.filter(callback)`
- `.map(callback)`
- `.forEach(callback)`
- `.contains(element)`
- [x] Stack traces
- [x] Add `FileDescriptor::write()`
- [x] Blank identifier does not bind
- [x] `String` methods
- `.contains(substring)`
- `.split(sep)`
- [x] Mock out standard I/O for tests
- [x] Reading from `$stdin`
- [x] `File::open()` -> `FileDescriptor`
- `fd.close()
- `fd.writeAll("Hello, World!\n")`
- [x] `throw` keyword
- [x] error handling (`let result = mayError() catch (e) DEFAULT;`)
- [x] $stdin, $stderr
- [x] CLI args
- [x] `&` and `&!` operators for spawning sub-processes
- [x] Implement exit function
- [x] Parser errors
- [x] $env
- [x] $script & $scriptDir
- [x] Mock out processes, files for unit tests
- [x] Context variables, with statements; $cwd
- [x] Break and continue statements
- [x] First class for loops
- [x] Migrate var declarations and reassignment to expressions
- [x] Allow single statement blocks to omit semicolons
- [x] Return statements
- [x] Files
- [x] raw string literals
- [x] operators are top level functions that DO one thing, but allow for implicit casting of its operands
- [x] Use readline for REPL
- [x] Class prototypes
- [x] Class static methods
- [x] Process class
- [x] README.md
- [x] Do blocks
- [x] For-in loops
- [x] Comments
- [x] Lists
- [x] Loops
- [x] Hashmaps
- [x] Updating lists
- [x] String interpolation
- [x] Implement all arithmetic operators
- [x] Automatic semicolon insertion
- [x] HashMap literals
- [x] Store locs in runtime values
- [x] Assertions
- [x] Classes
- [x] Assertions
- [x] Store locs in runtime values
- [x] HashMap literals
- [x] Automatic semicolon insertion
- [x] Implement all arithmetic operators
- [x] String interpolation
- [x] Updating lists
- [x] Hashmaps
- [x] Loops
- [x] Lists
- [x] Comments
- [x] For-in loops
- [x] Do blocks
- [x] README.md
- [x] Process class
- [x] Class static methods
- [x] Class prototypes
- [x] Use readline for REPL
- [x] operators are top level functions that DO one thing, but allow for implicit casting of its operands
- [x] raw string literals
- [x] Files
- [x] Return statements
- [x] Allow single statement blocks to omit semicolons
- [x] Migrate var declarations and reassignment to expressions
- [x] First class for loops
- [x] Break and continue statements
- [x] Context variables, with statements; $cwd
- [x] Mock out processes, files for unit tests
- [x] $script & $scriptDir
- [x] $env
- [x] Parser errors
- [x] Implement exit function
- [x] `&` and `&!` operators for spawning sub-processes
- [x] CLI args
- [x] $stdin, $stderr
- [x] error handling (`let result = mayError() catch (e) DEFAULT;`)
- [x] `throw` keyword
- [x] `File::open()` -> `FileDescriptor`
- `fd.writeAll("Hello, World!\n")`
- `fd.close()
- [x] Reading from `$stdin`
- [x] Mock out standard I/O for tests
- [x] `String` methods
- `.split(sep)`
- `.contains(substring)`
- [x] Blank identifier does not bind
- [x] Add `FileDescriptor::write()`
- [x] Stack traces
- [x] `List` methods
- `.contains(element)`
- `.forEach(callback)`
- `.map(callback)`
- `.filter(callback)`
- `.reduce(callback)`
- `.push(element)`
- `.pop(element)`
- [x] Bug: $scriptDir and $cwd should be absolute, so they can be chained
- [x] Recursion (depends on conditionals)
- [x] Infix functions
- [x] Tooling to diagnose shift/reduce conflicts
- [x] Conditionals
- [x] Invoking function expressions
- [x] Function arguments
- [x] Variable re-assignment
- [x] Closures
- [x] Lexical scoping

## Bugs
- [ ] bug: stacktrace printing is wrong in REPL
- [ ] bug: Do static methods need to take self as first arg?
4 changes: 2 additions & 2 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let repl cwd =
let globals =
Globals.make_globals
(module Native.Prod)
"" script_path ~env:(Core_unix.environment ()) ~argv:[]
"" script_path ~env:(Core_unix.environment ()) ~argv:[] ~version
in
let env = Compiler.Environment.create "" |> Compiler.Environment.populate in
let rec repl_inner globals env =
Expand Down Expand Up @@ -71,7 +71,7 @@ let interpreter program program_name argv =
let globals =
Globals.make_globals
(module Native.Prod)
~argv program program_name ~env:(Core_unix.environment ())
~argv program program_name ~env:(Core_unix.environment ()) ~version
in

let result =
Expand Down
7 changes: 7 additions & 0 deletions lib/common/common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ let debug_mode = false

(* See BUFSIZ in stdio.h = 8192; although apparently OCaml's IO buffers are 65536? *)
let bufsiz = 65536

(*
TODO generate this at build time, from git
https://discuss.ocaml.org/t/how-to-generate-different-code-based-on-an-environmental-variable-at-build-time/16104
*)
let version =
Semver.create ~major:0 ~minor:1 ~patch:0 () |> Core.Result.ok_or_failwith
72 changes: 72 additions & 0 deletions lib/common/semver.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
open Core

type t = { major : int; minor : int; patch : int }

(*
type constraint_t = Exact of t
*)

let create ?(major = 0) ?(minor = 0) ?(patch = 0) () =
let open Result.Monad_infix in
let verify_positive name i =
if i < 0 then
Error (Printf.sprintf "Field \"%s\" cannot be negative, got %d" name i)
else Ok ()
in
verify_positive "major" major >>= fun () ->
verify_positive "minor" minor >>= fun () ->
verify_positive "patch" patch >>= fun () -> Ok { major; minor; patch }

module Parser = struct
type state = Major | Minor of int | Patch of int * int | Done of t

let parse str =
let open Result.Monad_infix in
let str_len = String.length str in
let c_to_digit_opt c =
let i = Char.to_int c - 48 in
if i >= 0 && i <= 9 then Some i else None
in
let lex_dot i =
let next_i = i + 1 in
(* TODO validate *)
next_i
in
let lex_int idx =
let rec lex_int_rec prefix idx =
let ch = String.get str idx in
match c_to_digit_opt ch with
| Some i -> (
if idx + 1 >= str_len then Ok (idx + 1, prefix + i)
else
let next_digit_opt = String.get str (idx + 1) |> c_to_digit_opt in
match next_digit_opt with
| Some _ ->
(* TODO optimize *)
lex_int_rec Int.((prefix + i) * 10) (idx + 1)
| None -> Ok (lex_dot (idx + 1), prefix + i))
| None -> Error "parse error"
in
lex_int_rec 0 idx
in
let rec lex_rec idx = function
| Major ->
lex_int idx >>= fun (new_idx, major) -> lex_rec new_idx (Minor major)
| Minor major ->
lex_int idx >>= fun (new_idx, minor) ->
lex_rec new_idx (Patch (major, minor))
| Patch (major, minor) ->
lex_int idx >>= fun (new_idx, patch) ->
let t' = { major; minor; patch } in
lex_rec new_idx (Done t')
| Done t' -> Ok t'
in
lex_rec 0 Major
end

let parse str = Parser.parse str

let is_equal t1 t2 =
t1.major = t2.major && t1.minor = t2.minor && t1.patch = t2.patch

let to_string t' = Printf.sprintf "%d.%d.%d" t'.major t'.minor t'.patch
9 changes: 9 additions & 0 deletions lib/common/semver.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type t = private { major : int; minor : int; patch : int }

val parse : string -> (t, string) Result.t

val create :
?major:int -> ?minor:int -> ?patch:int -> unit -> (t, string) Result.t

val is_equal : t -> t -> bool
val to_string : t -> string
7 changes: 7 additions & 0 deletions lib/common/stdlib_interface.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let globals =
"$stderr";
"$stdin";
"$stdout";
"$version";
];
protos =
[
Expand Down Expand Up @@ -110,5 +111,11 @@ let globals =
setters = [];
static_getters = [];
};
{
name = "Version";
getters = [ "toString" ];
setters = [];
static_getters = [ "current" ];
};
];
}
7 changes: 5 additions & 2 deletions lib/interpreter/globals.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type t = {
stack_frames : Runtime.backtrace;
current_function_name : string;
(* Store this since stack_frames stores the name of the enclosing func *)
version : Sloth_common.Semver.t;
}

let push_frame t next_func pos =
Expand All @@ -24,7 +25,7 @@ let push_frame t next_func pos =
}

(* TODO memoize *)
let make_globals m src script_path ~argv ~env =
let make_globals m src script_path ~argv ~env ~version =
let module M = (val m : Native.Sig) in
let classes = Hashtbl.create (module String) in
let identifiers : Runtime.t Identifiers.t = Identifiers.create () in
Expand All @@ -38,7 +39,8 @@ let make_globals m src script_path ~argv ~env =

(* TODO inject cwd *)
List.iter
(Stdlib_impl.context_ids ~cwd:(Sys_unix.getcwd ()) ~env ~script_path ~argv)
(Stdlib_impl.context_ids ~cwd:(Sys_unix.getcwd ()) ~env ~script_path ~argv
~version)
~f:(fun (name, t) ->
Context.bind context_ids name t |> Option.value_exn ~message:__LOC__);

Expand Down Expand Up @@ -80,4 +82,5 @@ let make_globals m src script_path ~argv ~env =
argv;
stack_frames = [];
current_function_name = "(top-level)";
version;
}
Loading