Compare commits
4 Commits
27115ab4f2
...
34aeabdd99
| Author | SHA1 | Date | |
|---|---|---|---|
| 34aeabdd99 | |||
| fb47e6ecdd | |||
| 4faf309752 | |||
| 3f0a860a03 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
_build
|
_build
|
||||||
*~
|
*~
|
||||||
|
.direnv
|
||||||
|
result
|
||||||
|
|||||||
9
bin/dune
9
bin/dune
@@ -1,6 +1,5 @@
|
|||||||
(executable
|
(executable
|
||||||
(name main)
|
(name inter)
|
||||||
(public_name main)
|
(public_name ollisp-inter)
|
||||||
(libraries str lisp unix))
|
(libraries str unix interpreter)
|
||||||
(include_subdirs unqualified)
|
(package ollisp))
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
open Lisp.Ast;;
|
open Interpreter.Ast;;
|
||||||
open Printf;;
|
open Printf;;
|
||||||
open Lisp;;
|
open Interpreter;;
|
||||||
open Env;;
|
open Env;;
|
||||||
open Eval;;
|
open Eval;;
|
||||||
open Read;;
|
|
||||||
|
|
||||||
let () = InterpreterStdlib.init_default_env ()
|
let () = Stdlib.init_default_env ()
|
||||||
|
|
||||||
let rec repl env c =
|
let rec repl env c =
|
||||||
let () = printf ">>> "; Out_channel.flush Out_channel.stdout; in
|
let () = printf ">>> "; Out_channel.flush Out_channel.stdout; in
|
||||||
@@ -14,7 +13,7 @@ let rec repl env c =
|
|||||||
| Some "exit" -> ()
|
| Some "exit" -> ()
|
||||||
| Some l ->
|
| Some l ->
|
||||||
try
|
try
|
||||||
let vals = (parse_str l) in
|
let vals = (read_from_str l) in
|
||||||
(* dbg_print_all vals; *)
|
(* dbg_print_all vals; *)
|
||||||
pretty_print_all (eval_all env vals);
|
pretty_print_all (eval_all env vals);
|
||||||
Out_channel.flush Out_channel.stdout;
|
Out_channel.flush Out_channel.stdout;
|
||||||
@@ -23,7 +22,7 @@ let rec repl env c =
|
|||||||
| Invalid_argument s ->
|
| Invalid_argument s ->
|
||||||
printf "%s\nResuming repl\n" s;
|
printf "%s\nResuming repl\n" s;
|
||||||
repl env c
|
repl env c
|
||||||
| Parser.Error ->
|
| Parser.Parse.Error ->
|
||||||
printf "Expression '%s' couldn't be parsed, try again\n" l;
|
printf "Expression '%s' couldn't be parsed, try again\n" l;
|
||||||
repl env c
|
repl env c
|
||||||
;;
|
;;
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
(lang dune 3.7)
|
(lang dune 3.7)
|
||||||
(using menhir 2.1)
|
(using menhir 2.1)
|
||||||
|
|
||||||
|
(package
|
||||||
|
(name ollisp))
|
||||||
|
|||||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764950072,
|
||||||
|
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f61125a668a320878494449750330ca58b78c557",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
27
flake.nix
Normal file
27
flake.nix
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
description = "a lisp interpreter/compiler in ocaml";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {self, nixpkgs}:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.x86_64-linux.default = pkgs.ocamlPackages.buildDunePackage {
|
||||||
|
pname = "ollisp";
|
||||||
|
version = "0.0.1";
|
||||||
|
src = pkgs.lib.cleanSource ./.;
|
||||||
|
preBuildPhase = "ls -R";
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
ocamlPackages.menhir
|
||||||
|
];
|
||||||
|
};
|
||||||
|
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = with pkgs.ocamlPackages; [
|
||||||
|
menhir merlin dune_3
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
25
lib/compiler/compilation.ml
Normal file
25
lib/compiler/compilation.ml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
open Parser.Ast;;
|
||||||
|
|
||||||
|
(* This type represents an intermediate step between the AST and opcodes in our
|
||||||
|
compiler. We need this extra step to resolve addresses, e.g. how do you know
|
||||||
|
what exact address an if expression needs to jump to before you compile it?
|
||||||
|
you don't, you just keep a symbolic label there, resolve later.
|
||||||
|
*)
|
||||||
|
type intermediate_opcode =
|
||||||
|
| ISelect of string * string
|
||||||
|
| ILDF of string
|
||||||
|
| ILD of int (* an index into the constant table *)
|
||||||
|
| INil
|
||||||
|
| IRet
|
||||||
|
| IAdd
|
||||||
|
| IJoin
|
||||||
|
| ILabel of string (* does not emit any byte code *)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(* TODO: Complete *)
|
||||||
|
let (compile : lisp_ast -> intermediate_opcode list) = function
|
||||||
|
| LInt x -> [ILD x]
|
||||||
|
| _ -> [];;
|
||||||
3
lib/compiler/dune
Normal file
3
lib/compiler/dune
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
(library
|
||||||
|
(name compiler)
|
||||||
|
(libraries parser))
|
||||||
7
lib/dune
7
lib/dune
@@ -1,7 +0,0 @@
|
|||||||
(library
|
|
||||||
(name lisp))
|
|
||||||
|
|
||||||
(include_subdirs unqualified)
|
|
||||||
|
|
||||||
(menhir (modules parser))
|
|
||||||
(ocamllex lexer)
|
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
|
||||||
|
(* This is different from the lisp_ast data returned by the parser!
|
||||||
|
We will first need to translate that into this in order to use it.
|
||||||
|
This representation includes things that can only occur during runtime,
|
||||||
|
like the various kinds of functions and macros.
|
||||||
|
|
||||||
|
Additionally, since this is an interpreter, macros tend to be a little
|
||||||
|
awkward in that they behave exactly like the macro gets expanded just
|
||||||
|
before the result gets executed. This is different from the compiled
|
||||||
|
behaviour where the macro is evaluated at compile time.
|
||||||
|
|
||||||
|
Though of course, with the dynamic nature of lisp, and its capability
|
||||||
|
to compile more code at runtime, there will naturally be complications.
|
||||||
|
*)
|
||||||
type lisp_val =
|
type lisp_val =
|
||||||
| LInt of int
|
| LInt of int
|
||||||
| LDouble of float
|
| LDouble of float
|
||||||
@@ -16,8 +30,8 @@ type lisp_val =
|
|||||||
| LFunction of string * environment * lisp_val * lisp_val
|
| LFunction of string * environment * lisp_val * lisp_val
|
||||||
| LLambda of environment * lisp_val * lisp_val
|
| LLambda of environment * lisp_val * lisp_val
|
||||||
(* a macro is exactly the same as a function, with the distinction
|
(* a macro is exactly the same as a function, with the distinction
|
||||||
that it receives all of its arguments completely unevaluated
|
that it receives all of its arguments completely unevaluated
|
||||||
in a compiled lisp this would probably make more of a difference *)
|
*)
|
||||||
| LMacro of string * environment * lisp_val * lisp_val
|
| LMacro of string * environment * lisp_val * lisp_val
|
||||||
| LUnnamedMacro of environment * lisp_val * lisp_val
|
| LUnnamedMacro of environment * lisp_val * lisp_val
|
||||||
| LQuoted of lisp_val
|
| LQuoted of lisp_val
|
||||||
@@ -113,3 +127,16 @@ let pretty_print_all vs =
|
|||||||
let dbg_print_all vs =
|
let dbg_print_all vs =
|
||||||
let pr v = Printf.printf "%s\n" (dbg_print_one v) in
|
let pr v = Printf.printf "%s\n" (dbg_print_one v) in
|
||||||
List.iter pr vs
|
List.iter pr vs
|
||||||
|
|
||||||
|
|
||||||
|
let rec convert_one = function
|
||||||
|
| Parser.Ast.LInt x -> LInt x
|
||||||
|
| Parser.Ast.LDouble x -> LDouble x
|
||||||
|
| Parser.Ast.LNil -> LNil
|
||||||
|
| Parser.Ast.LString s -> LString s
|
||||||
|
| Parser.Ast.LSymbol s -> LSymbol s
|
||||||
|
| Parser.Ast.LCons (a, b) -> LCons (convert_one a, convert_one b)
|
||||||
|
|
||||||
|
|
||||||
|
let read_from_str s =
|
||||||
|
List.map convert_one (Parser.parse_str s)
|
||||||
4
lib/interpreter/dune
Normal file
4
lib/interpreter/dune
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
(library
|
||||||
|
(name interpreter)
|
||||||
|
(libraries parser)
|
||||||
|
(package ollisp))
|
||||||
@@ -200,5 +200,5 @@ let init_default_env () =
|
|||||||
Idea: maybe put this in a file instead of putting
|
Idea: maybe put this in a file instead of putting
|
||||||
literally the entire standard library in a constant string
|
literally the entire standard library in a constant string
|
||||||
*)
|
*)
|
||||||
ignore (Eval.eval_all default_env (Read.parse_str init_script));
|
ignore (Eval.eval_all default_env (read_from_str init_script));
|
||||||
()
|
()
|
||||||
10
lib/parser/ast.ml
Normal file
10
lib/parser/ast.ml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
type lisp_ast =
|
||||||
|
| LInt of int
|
||||||
|
| LDouble of float
|
||||||
|
| LSymbol of string
|
||||||
|
| LString of string
|
||||||
|
| LNil
|
||||||
|
| LCons of lisp_ast * lisp_ast
|
||||||
|
|
||||||
7
lib/parser/dune
Normal file
7
lib/parser/dune
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
(library
|
||||||
|
(name parser)
|
||||||
|
(modules parser lex parse ast)
|
||||||
|
(package ollisp))
|
||||||
|
|
||||||
|
(menhir (modules parse))
|
||||||
|
(ocamllex lex)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
open Lexing
|
open Lexing
|
||||||
open Parser
|
open Parse
|
||||||
exception SyntaxError of string
|
exception SyntaxError of string
|
||||||
|
|
||||||
let strip_quotes s = String.sub s 1 (String.length s - 2);;
|
let strip_quotes s = String.sub s 1 (String.length s - 2);;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
%token DOT
|
%token DOT
|
||||||
%token EOF
|
%token EOF
|
||||||
|
|
||||||
%start <Ast.lisp_val option> prog
|
%start <lisp_ast option> prog
|
||||||
%%
|
%%
|
||||||
|
|
||||||
prog:
|
prog:
|
||||||
@@ -22,7 +22,7 @@ prog:
|
|||||||
|
|
||||||
expr:
|
expr:
|
||||||
| i = INT { LInt i }
|
| i = INT { LInt i }
|
||||||
| d = DOUBLE {LDouble d}
|
| d = DOUBLE { LDouble d}
|
||||||
| s = SYM { LSymbol s }
|
| s = SYM { LSymbol s }
|
||||||
| s = STR { LString (String.uppercase_ascii s) }
|
| s = STR { LString (String.uppercase_ascii s) }
|
||||||
| LPAREN; l = lisp_list_rest { l }
|
| LPAREN; l = lisp_list_rest { l }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
let parse_one lb = Parser.prog (Lexer.read) lb
|
let parse_one lb = Parse.prog (Lex.read) lb
|
||||||
|
|
||||||
let parse lb =
|
let parse lb =
|
||||||
let rec helper () =
|
let rec helper () =
|
||||||
@@ -11,3 +11,6 @@ let parse lb =
|
|||||||
let parse_str s =
|
let parse_str s =
|
||||||
parse (Lexing.from_string s)
|
parse (Lexing.from_string s)
|
||||||
|
|
||||||
|
|
||||||
|
module Ast = Ast
|
||||||
|
module Parse = Parse
|
||||||
Reference in New Issue
Block a user