mirror of
https://github.com/LucasVbr/croissant.git
synced 2026-07-09 06:57:48 +00:00
refactor: Syntax with unit tests
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
;bin/dune
|
|
||||||
|
|
||||||
(executable
|
(executable
|
||||||
(name main)
|
(name main)
|
||||||
(public_name croissant)
|
(public_name croissant)
|
||||||
(libraries ast))
|
(libraries Croissant))
|
||||||
|
|||||||
+22
-17
@@ -1,18 +1,23 @@
|
|||||||
(* bin/main.ml *)
|
|
||||||
|
|
||||||
open Printf
|
|
||||||
open Ast
|
|
||||||
|
|
||||||
exception Error of string
|
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
(* Array.iteri (fun i arg -> printf "%d: %s\n" i arg) Sys.argv; *)
|
let file_path = Sys.argv.(1) in
|
||||||
let help_message = "Usage: croissant <file_path>\n"
|
let file_stream = open_in file_path in
|
||||||
and args_count = Array.length Sys.argv - 1 in
|
let lexbuf = Lexing.from_channel file_stream in
|
||||||
if args_count >= 2 then printf "%s" help_message
|
let ast =
|
||||||
else if args_count = 1 then
|
try Analyzer.Parser.main Analyzer.Lexer.token lexbuf with
|
||||||
let file_path = Sys.argv.(1) in
|
| Analyzer.Lexer.Error c ->
|
||||||
let ast = Print.string_of_source_file (Analyzer.analyze_file file_path) in
|
let file_name = lexbuf.lex_curr_p.pos_fname
|
||||||
printf "%s\n" ast
|
and line_num = lexbuf.lex_curr_p.pos_lnum
|
||||||
else raise (Error "interpreter from stdin is not implemented yet")
|
and col_num = lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol in
|
||||||
(* TODO: Implement interpreter from stdin *)
|
Printf.fprintf stderr "Fichier \"%s\", ligne %d, colonne %d\n%s: %s\n"
|
||||||
|
file_name line_num col_num "Erreur lexicale"
|
||||||
|
("Caractère '" ^ String.make 1 c ^ "' inconnu");
|
||||||
|
exit 1
|
||||||
|
| Analyzer.Parser.Error ->
|
||||||
|
let file_name = lexbuf.lex_curr_p.pos_fname
|
||||||
|
and line_num = lexbuf.lex_curr_p.pos_lnum
|
||||||
|
and col_num = lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol in
|
||||||
|
Printf.fprintf stderr "Fichier \"%s\", ligne %d, colonne %d\n%s: %s\n"
|
||||||
|
file_name line_num col_num "Erreur syntaxique" "Syntaxe incorrecte";
|
||||||
|
exit 1
|
||||||
|
in
|
||||||
|
Printf.printf "%s\n" (Syntax.SourceFiles.pp_source_files ast)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
(library
|
||||||
|
(name Analyzer)
|
||||||
|
(modules lexer parser)
|
||||||
|
(libraries Syntax))
|
||||||
|
|
||||||
|
(menhir
|
||||||
|
(modules parser))
|
||||||
|
|
||||||
|
(ocamllex
|
||||||
|
(modules lexer))
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
open Parser
|
||||||
|
exception Error of char
|
||||||
|
|
||||||
|
let buffer = Buffer.create 256
|
||||||
|
}
|
||||||
|
|
||||||
|
let white_space = [' ' '\t' '\n']
|
||||||
|
|
||||||
|
let letter = ['a'-'z' 'A'-'Z']
|
||||||
|
let digit = ['0'-'9']
|
||||||
|
let alphanum = letter | digit
|
||||||
|
let non_digit = '_'
|
||||||
|
|
||||||
|
let identifier = letter (alphanum | non_digit)*
|
||||||
|
let interger = digit+
|
||||||
|
let float = digit+ '.' digit+
|
||||||
|
|
||||||
|
rule token = parse
|
||||||
|
| white_space { token lexbuf }
|
||||||
|
| '\n' { Lexing.new_line lexbuf; token lexbuf }
|
||||||
|
|
||||||
|
(* Comments *)
|
||||||
|
| "//" { line_comment lexbuf }
|
||||||
|
| "/*" { block_comment lexbuf }
|
||||||
|
|
||||||
|
(* Delimiters *)
|
||||||
|
| '(' { LPAREN }
|
||||||
|
| ')' { RPAREN }
|
||||||
|
| ';' { SEMICOLON }
|
||||||
|
|
||||||
|
(* Operators *)
|
||||||
|
| '+' { PLUS }
|
||||||
|
| '-' { MINUS }
|
||||||
|
| '*' { TIMES }
|
||||||
|
| '/' { DIV }
|
||||||
|
|
||||||
|
(* Keywords *)
|
||||||
|
(* ... *)
|
||||||
|
|
||||||
|
(* Literals *)
|
||||||
|
| interger as i { INT (int_of_string i) }
|
||||||
|
(* | float as f { FLOAT (float_of_string f) } *)
|
||||||
|
|
||||||
|
(* Identifiers *)
|
||||||
|
(* ... *)
|
||||||
|
|
||||||
|
| eof { EOF }
|
||||||
|
| _ as c { raise (Error c) }
|
||||||
|
|
||||||
|
and string = parse
|
||||||
|
| '"' { token lexbuf }
|
||||||
|
| '\\' { Buffer.add_char buffer '\\'; string lexbuf }
|
||||||
|
| '\n' { Buffer.add_char buffer '\n'; string lexbuf }
|
||||||
|
| eof { raise (Error '"') }
|
||||||
|
| _ { Buffer.add_char buffer (Lexing.lexeme_char lexbuf 0); string lexbuf }
|
||||||
|
|
||||||
|
and line_comment = parse
|
||||||
|
| '\n' { Lexing.new_line lexbuf; token lexbuf }
|
||||||
|
| eof { EOF }
|
||||||
|
| _ { line_comment lexbuf }
|
||||||
|
|
||||||
|
and block_comment = parse
|
||||||
|
| "*/" { token lexbuf }
|
||||||
|
| '\n' { Lexing.new_line lexbuf; block_comment lexbuf }
|
||||||
|
| eof { EOF }
|
||||||
|
| _ { block_comment lexbuf }
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
%{
|
||||||
|
open Syntax
|
||||||
|
%}
|
||||||
|
|
||||||
|
%token <int> INT
|
||||||
|
%token PLUS "+"
|
||||||
|
%token MINUS "-"
|
||||||
|
%token TIMES "*"
|
||||||
|
%token DIV "/"
|
||||||
|
|
||||||
|
%token SEMICOLON ";"
|
||||||
|
|
||||||
|
%token LPAREN "("
|
||||||
|
%token RPAREN ")"
|
||||||
|
|
||||||
|
%token EOF
|
||||||
|
|
||||||
|
%left PLUS MINUS
|
||||||
|
%left TIMES DIV
|
||||||
|
|
||||||
|
%nonassoc UMINUS
|
||||||
|
|
||||||
|
%start <SourceFiles.source_files> main
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
main:
|
||||||
|
| statements EOF { SourceFiles.SourceFile $1 }
|
||||||
|
|
||||||
|
statements:
|
||||||
|
| statement ";" { $1 }
|
||||||
|
| statement ";" statements { Statements.SequenceStatement ($1, $3) }
|
||||||
|
|
||||||
|
statement:
|
||||||
|
| expression { Statements.ExpressionStatement($1) }
|
||||||
|
|
||||||
|
expression:
|
||||||
|
| literal { Expressions.Literal($1) }
|
||||||
|
| unary_expression { $1 }
|
||||||
|
| binary_expression { $1 }
|
||||||
|
| "(" expression ")" { $2 }
|
||||||
|
|
||||||
|
literal:
|
||||||
|
| INT { Literals.Integer($1) }
|
||||||
|
|
||||||
|
unary_expression:
|
||||||
|
| "-" expression %prec UMINUS { Expressions.UnaryExpression(UnaryOperators.ArithmeticNegation, $2) }
|
||||||
|
|
||||||
|
binary_expression:
|
||||||
|
| expression "+" expression { Expressions.BinaryExpression (BinaryOperators.Add, $1, $3) }
|
||||||
|
| expression "-" expression { Expressions.BinaryExpression (BinaryOperators.Subtract, $1, $3) }
|
||||||
|
| expression "*" expression { Expressions.BinaryExpression (BinaryOperators.Multiply, $1, $3) }
|
||||||
|
| expression "/" expression { Expressions.BinaryExpression (BinaryOperators.Divide, $1, $3) }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
type environment = { variables : (string * _type) list }
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
open Printf
|
|
||||||
open Typecheck
|
|
||||||
|
|
||||||
let analyze lexbuf =
|
|
||||||
let ast =
|
|
||||||
try Parser.main Lexer.token lexbuf with
|
|
||||||
| Lexer.Error c ->
|
|
||||||
let file_name = lexbuf.lex_curr_p.pos_fname
|
|
||||||
and line_num = lexbuf.lex_curr_p.pos_lnum
|
|
||||||
and col_num = lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol in
|
|
||||||
fprintf stderr "Fichier \"%s\", ligne %d, colonne %d\n%s: %s\n"
|
|
||||||
file_name line_num col_num "Erreur lexicale"
|
|
||||||
("Caractère '" ^ String.make 1 c ^ "' inconnu");
|
|
||||||
exit 1
|
|
||||||
| Parser.Error ->
|
|
||||||
let file_name = lexbuf.lex_curr_p.pos_fname
|
|
||||||
and line_num = lexbuf.lex_curr_p.pos_lnum
|
|
||||||
and col_num = lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol in
|
|
||||||
fprintf stderr "Fichier \"%s\", ligne %d, colonne %d\n%s: %s\n"
|
|
||||||
file_name line_num col_num "Erreur syntaxique" "Syntaxe incorrecte";
|
|
||||||
exit 1
|
|
||||||
in
|
|
||||||
let _ = type_of_source_file ast in
|
|
||||||
ast
|
|
||||||
|
|
||||||
let analyze_file file_name =
|
|
||||||
let file_stream = open_in file_name in
|
|
||||||
let lexbuf = Lexing.from_channel file_stream in
|
|
||||||
Lexing.set_filename lexbuf file_name;
|
|
||||||
analyze lexbuf
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
(library
|
|
||||||
(name ast)
|
|
||||||
(modules lexer parser syntax print typecheck analyzer)
|
|
||||||
)
|
|
||||||
|
|
||||||
(menhir
|
|
||||||
(modules parser))
|
|
||||||
|
|
||||||
(ocamllex
|
|
||||||
(modules lexer))
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
(* lib/lexer.mll *)
|
|
||||||
{
|
|
||||||
open Parser
|
|
||||||
exception Error of char
|
|
||||||
|
|
||||||
let buffer = Buffer.create 256
|
|
||||||
}
|
|
||||||
|
|
||||||
let letter = ['a'-'z' 'A'-'Z']
|
|
||||||
let digit = ['0'-'9']
|
|
||||||
let alphanum = letter | digit
|
|
||||||
let non_digit = '_'
|
|
||||||
|
|
||||||
let identifier = letter (alphanum | non_digit)*
|
|
||||||
let integer = digit+
|
|
||||||
let float = digit* '.' digit+
|
|
||||||
|
|
||||||
rule token = parse
|
|
||||||
| ' ' | '\t' { token lexbuf }
|
|
||||||
| "/*" { block_comment lexbuf }
|
|
||||||
| "//" { line_comment lexbuf }
|
|
||||||
| '\n' { Lexing.new_line lexbuf; token lexbuf }
|
|
||||||
|
|
||||||
| '+' { PLUS }
|
|
||||||
| '-' { MINUS }
|
|
||||||
| '*' { TIMES }
|
|
||||||
| '/' { DIVIDE }
|
|
||||||
| "&&" { AMPERSAND_AMPERSAND }
|
|
||||||
| "||" { BAR_BAR }
|
|
||||||
| "==" { EQUALS_EQUALS }
|
|
||||||
| "!=" { EXCLAMATION_EQUALS }
|
|
||||||
| '<' { LESS_THAN }
|
|
||||||
| "<=" { LESS_THAN_EQUALS }
|
|
||||||
| '>' { GREATER_THAN }
|
|
||||||
| ">=" { GREATER_THAN_EQUALS }
|
|
||||||
| '=' { EQUALS }
|
|
||||||
|
|
||||||
| "!" { EXCLAMATION }
|
|
||||||
| ';' { SEMICOLON }
|
|
||||||
| ':' { COLON }
|
|
||||||
| ',' { COMMA }
|
|
||||||
|
|
||||||
| '(' { LPAREN }
|
|
||||||
| ')' { RPAREN }
|
|
||||||
|
|
||||||
| "var" { VAR }
|
|
||||||
| "vrai" { BOOLEAN(true) }
|
|
||||||
| "faux" { BOOLEAN(false) }
|
|
||||||
| "nul" { NULL }
|
|
||||||
|
|
||||||
| "entier" { INTEGER_TYPE }
|
|
||||||
| "reel" { FLOAT_TYPE }
|
|
||||||
| "caractere" { CHARACTER_TYPE }
|
|
||||||
| "chaine" { STRING_TYPE }
|
|
||||||
| "booleen" { BOOLEAN_TYPE }
|
|
||||||
| "vide" { VOID_TYPE }
|
|
||||||
|
|
||||||
| integer as lxm { INTEGER(int_of_string lxm) }
|
|
||||||
| float as lxm { FLOAT(float_of_string lxm) }
|
|
||||||
|
|
||||||
| "'\\''" { CHARACTER '\'' }
|
|
||||||
| "'\\n'" { CHARACTER '\n' }
|
|
||||||
| "'\\t'" { CHARACTER '\t' }
|
|
||||||
| "'\\\\'" { CHARACTER '\\' }
|
|
||||||
| "'\\r'" { CHARACTER '\r' }
|
|
||||||
| "'\\b'" { CHARACTER '\b' }
|
|
||||||
| "'" [^'\\'] "'" { CHARACTER (String.get (Lexing.lexeme lexbuf) 1) }
|
|
||||||
|
|
||||||
| "\"" { Buffer.clear buffer; string lexbuf }
|
|
||||||
| identifier as lxm { IDENTIFIER(lxm) }
|
|
||||||
|
|
||||||
| eof { EOF }
|
|
||||||
| _ as c { raise (Error c) }
|
|
||||||
|
|
||||||
and string = parse
|
|
||||||
| "\"" { STRING(Buffer.contents buffer)}
|
|
||||||
| "\\\"" { Buffer.add_char buffer '"'; string lexbuf }
|
|
||||||
| '\\' { Buffer.add_char buffer '\\'; string lexbuf }
|
|
||||||
| _ as c { Buffer.add_char buffer c; string lexbuf }
|
|
||||||
|
|
||||||
and line_comment = parse
|
|
||||||
| '\n' { Lexing.new_line lexbuf; token lexbuf }
|
|
||||||
| eof { EOF }
|
|
||||||
| _ { line_comment lexbuf }
|
|
||||||
|
|
||||||
and block_comment = parse
|
|
||||||
| "*/" { token lexbuf }
|
|
||||||
| '\n' { Lexing.new_line lexbuf; block_comment lexbuf }
|
|
||||||
| eof { EOF }
|
|
||||||
| _ { block_comment lexbuf }
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/* lib/parser.mly */
|
|
||||||
%{
|
|
||||||
open Syntax
|
|
||||||
%}
|
|
||||||
|
|
||||||
%token <int> INTEGER
|
|
||||||
%token <float> FLOAT
|
|
||||||
%token <bool> BOOLEAN
|
|
||||||
%token <char> CHARACTER
|
|
||||||
%token <string> STRING
|
|
||||||
%token <string> IDENTIFIER
|
|
||||||
%token NULL
|
|
||||||
|
|
||||||
%token INTEGER_TYPE
|
|
||||||
%token FLOAT_TYPE
|
|
||||||
%token CHARACTER_TYPE
|
|
||||||
%token STRING_TYPE
|
|
||||||
%token BOOLEAN_TYPE
|
|
||||||
%token VOID_TYPE
|
|
||||||
|
|
||||||
%token PLUS "+"
|
|
||||||
%token MINUS "-"
|
|
||||||
%token TIMES "*"
|
|
||||||
%token DIVIDE "/"
|
|
||||||
%token AMPERSAND_AMPERSAND "&&"
|
|
||||||
%token BAR_BAR "||"
|
|
||||||
%token EXCLAMATION "!"
|
|
||||||
%token EQUALS_EQUALS "=="
|
|
||||||
%token EXCLAMATION_EQUALS "!="
|
|
||||||
%token LESS_THAN "<"
|
|
||||||
%token LESS_THAN_EQUALS "<="
|
|
||||||
%token GREATER_THAN ">"
|
|
||||||
%token GREATER_THAN_EQUALS ">="
|
|
||||||
%token EQUALS "="
|
|
||||||
|
|
||||||
%token LPAREN "("
|
|
||||||
%token RPAREN ")"
|
|
||||||
|
|
||||||
%token SEMICOLON ";"
|
|
||||||
%token COLON ":"
|
|
||||||
%token COMMA ","
|
|
||||||
|
|
||||||
%token VAR "var"
|
|
||||||
|
|
||||||
%token EOF
|
|
||||||
|
|
||||||
%left "&&" "||"
|
|
||||||
%left "+" "-"
|
|
||||||
%left "*" "/"
|
|
||||||
%nonassoc UMINUS
|
|
||||||
|
|
||||||
%start main
|
|
||||||
%type <source_file> main
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
main:
|
|
||||||
| statements EOF { SourceFile($1) }
|
|
||||||
| EOF { SourceFile([]) }
|
|
||||||
|
|
||||||
statements:
|
|
||||||
| statement ";" { [$1] }
|
|
||||||
| statement ";" statements { $1 :: $3 }
|
|
||||||
|
|
||||||
statement:
|
|
||||||
| expression { ExpressionStatement($1) }
|
|
||||||
| "var" variable_declaration_list { VariableStatement($2) }
|
|
||||||
|
|
||||||
variable_declaration_list:
|
|
||||||
| variable_declaration { [$1] }
|
|
||||||
| variable_declaration "," variable_declaration_list { $1 :: $3 }
|
|
||||||
|
|
||||||
variable_declaration:
|
|
||||||
| IDENTIFIER ":" tp "=" expression { VariableDeclaration($3, Identifier($1), $5) }
|
|
||||||
| IDENTIFIER ":" tp { VariableDeclaration($3, Identifier($1), Literal(Null)) }
|
|
||||||
|
|
||||||
tp:
|
|
||||||
| INTEGER_TYPE { IntegerType }
|
|
||||||
| FLOAT_TYPE { FloatType }
|
|
||||||
| CHARACTER_TYPE { CharacterType }
|
|
||||||
| STRING_TYPE { StringType }
|
|
||||||
| BOOLEAN_TYPE { BooleanType }
|
|
||||||
| VOID_TYPE { VoidType }
|
|
||||||
|
|
||||||
expression:
|
|
||||||
| literal { Literal($1) }
|
|
||||||
| IDENTIFIER { Identifier($1) }
|
|
||||||
| unary_expression { $1 }
|
|
||||||
| binary_expression { $1 }
|
|
||||||
| "(" expression ")" { $2 }
|
|
||||||
|
|
||||||
literal:
|
|
||||||
| INTEGER { Integer($1) }
|
|
||||||
| FLOAT { Float($1) }
|
|
||||||
| CHARACTER { Character($1) }
|
|
||||||
| STRING { String($1) }
|
|
||||||
| BOOLEAN { Boolean($1) }
|
|
||||||
| NULL { Null }
|
|
||||||
|
|
||||||
unary_expression:
|
|
||||||
| "-" expression %prec UMINUS { UnaryExpression(Negate, $2) }
|
|
||||||
| "!" expression { UnaryExpression(Not, $2) }
|
|
||||||
|
|
||||||
binary_expression:
|
|
||||||
| e1=expression "+" e2=expression { BinaryExpression(Add, e1, e2) }
|
|
||||||
| e1=expression "-" e2=expression { BinaryExpression(Subtract, e1, e2) }
|
|
||||||
| e1=expression "*" e2=expression { BinaryExpression(Multiply, e1, e2) }
|
|
||||||
| e1=expression "/" e2=expression { BinaryExpression(Divide, e1, e2) }
|
|
||||||
| e1=expression "&&" e2=expression { BinaryExpression(AmpersandAmpersand, e1, e2) }
|
|
||||||
| e1=expression "||" e2=expression { BinaryExpression(BarBar, e1, e2) }
|
|
||||||
| e1=expression "==" e2=expression { BinaryExpression(EqualsEquals, e1, e2) }
|
|
||||||
| e1=expression "!=" e2=expression { BinaryExpression(ExclamationEquals, e1, e2) }
|
|
||||||
| e1=expression "<" e2=expression { BinaryExpression(LessThan, e1, e2) }
|
|
||||||
| e1=expression "<=" e2=expression { BinaryExpression(LessThanEquals, e1, e2) }
|
|
||||||
| e1=expression ">" e2=expression { BinaryExpression(GreaterThan, e1, e2) }
|
|
||||||
| e1=expression ">=" e2=expression { BinaryExpression(GreaterThanEquals, e1, e2) }
|
|
||||||
| e1=expression "=" e2=expression { BinaryExpression(Assign, e1, e2) }
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
(* lib/ast/print.ml *)
|
|
||||||
|
|
||||||
open Syntax
|
|
||||||
|
|
||||||
(** [string_of_type t] returns a string representation of the type [t]. *)
|
|
||||||
let string_of_type = function
|
|
||||||
| IntegerType -> "IntegerType"
|
|
||||||
| FloatType -> "FloatType"
|
|
||||||
| CharacterType -> "CharacterType"
|
|
||||||
| StringType -> "StringType"
|
|
||||||
| BooleanType -> "BooleanType"
|
|
||||||
| VoidType -> "VoidType"
|
|
||||||
|
|
||||||
(** [string_of_literal l] returns a string representation of the literal [l]. *)
|
|
||||||
let string_of_literal = function
|
|
||||||
| Integer i -> "Integer(" ^ string_of_int i ^ ")"
|
|
||||||
| Float f -> "Float(" ^ string_of_float f ^ ")"
|
|
||||||
| Character c -> "Character('" ^ Char.escaped c ^ "')"
|
|
||||||
| String s -> "String(\"" ^ String.escaped s ^ "\")"
|
|
||||||
| Boolean b -> "Boolean(" ^ string_of_bool b ^ ")"
|
|
||||||
| Null -> "Null"
|
|
||||||
|
|
||||||
(** [string_of_binary_operator op] returns a string representation of the binary operator [op]. *)
|
|
||||||
let string_of_binary_operator = function
|
|
||||||
| Add -> "Add"
|
|
||||||
| Subtract -> "Subtract"
|
|
||||||
| Multiply -> "Multiply"
|
|
||||||
| Divide -> "Divide"
|
|
||||||
| AmpersandAmpersand -> "AmpersandAmpersand"
|
|
||||||
| BarBar -> "BarBar"
|
|
||||||
| EqualsEquals -> "EqualsEquals"
|
|
||||||
| ExclamationEquals -> "ExclamationEquals"
|
|
||||||
| LessThan -> "LessThan"
|
|
||||||
| LessThanEquals -> "LessThanEquals"
|
|
||||||
| GreaterThan -> "GreaterThan"
|
|
||||||
| GreaterThanEquals -> "GreaterThanEquals"
|
|
||||||
| Assign -> "Assign"
|
|
||||||
|
|
||||||
(** [string_of_unary_operator op] returns a string representation of the unary operator [op]. *)
|
|
||||||
let string_of_unary_operator = function Negate -> "Negate" | Not -> "Not"
|
|
||||||
|
|
||||||
(** [string_of_expression e] returns a string representation of the expression [e]. *)
|
|
||||||
let rec string_of_expression = function
|
|
||||||
| Literal l -> "Literal(" ^ string_of_literal l ^ ")"
|
|
||||||
| Identifier i -> "Identifier(\"" ^ i ^ "\")"
|
|
||||||
| UnaryExpression (op, e) ->
|
|
||||||
"UnaryExpression("
|
|
||||||
^ string_of_unary_operator op
|
|
||||||
^ ", " ^ string_of_expression e ^ ")"
|
|
||||||
| BinaryExpression (op, e1, e2) ->
|
|
||||||
"BinaryExpression("
|
|
||||||
^ string_of_binary_operator op
|
|
||||||
^ ", " ^ string_of_expression e1 ^ ", " ^ string_of_expression e2 ^ ")"
|
|
||||||
|
|
||||||
let string_of_variable_declaration = function
|
|
||||||
| VariableDeclaration (t, id, e) ->
|
|
||||||
"VariableDeclaration(" ^ string_of_type t ^ ", " ^ string_of_expression id
|
|
||||||
^ ", " ^ string_of_expression e ^ ")"
|
|
||||||
|
|
||||||
(** [string_of_statement s] returns a string representation of the statement [s]. *)
|
|
||||||
let string_of_statement = function
|
|
||||||
| ExpressionStatement e ->
|
|
||||||
"ExpressionStatement(" ^ string_of_expression e ^ ")"
|
|
||||||
| VariableStatement decls ->
|
|
||||||
let decl_strings = List.map string_of_variable_declaration decls in
|
|
||||||
"VariableStatement([" ^ String.concat ", " decl_strings ^ "])"
|
|
||||||
|
|
||||||
(** [string_of_source_file f] returns a string representation of the source file [f]. *)
|
|
||||||
let string_of_source_file = function
|
|
||||||
| SourceFile stmts ->
|
|
||||||
let stmt_strings = List.map string_of_statement stmts in
|
|
||||||
"SourceFile([" ^ String.concat ", " stmt_strings ^ "])"
|
|
||||||
|
|
||||||
(** The signature of the module [Print]. *)
|
|
||||||
module type Print = sig
|
|
||||||
val string_of_type : _type -> string
|
|
||||||
val string_of_binary_operator : binary_operator -> string
|
|
||||||
val string_of_unary_operator : unary_operator -> string
|
|
||||||
val string_of_literal : literal -> string
|
|
||||||
val string_of_expression : expression -> string
|
|
||||||
val string_of_variable_declaration : variable_declaration -> string
|
|
||||||
val string_of_statement : statement -> string
|
|
||||||
val string_of_source_file : source_file -> string
|
|
||||||
end
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
(* lib/ast/syntax.ml *)
|
|
||||||
|
|
||||||
type _type =
|
|
||||||
| IntegerType
|
|
||||||
| FloatType
|
|
||||||
| CharacterType
|
|
||||||
| StringType
|
|
||||||
| BooleanType
|
|
||||||
| VoidType
|
|
||||||
|
|
||||||
type literal =
|
|
||||||
| Integer of int
|
|
||||||
| Float of float
|
|
||||||
| Character of char
|
|
||||||
| String of string
|
|
||||||
| Boolean of bool
|
|
||||||
| Null
|
|
||||||
|
|
||||||
type binary_operator =
|
|
||||||
| Add
|
|
||||||
| Subtract
|
|
||||||
| Multiply
|
|
||||||
| Divide
|
|
||||||
| AmpersandAmpersand
|
|
||||||
| BarBar
|
|
||||||
| EqualsEquals
|
|
||||||
| ExclamationEquals
|
|
||||||
| LessThan
|
|
||||||
| LessThanEquals
|
|
||||||
| GreaterThan
|
|
||||||
| GreaterThanEquals
|
|
||||||
| Assign
|
|
||||||
|
|
||||||
type unary_operator = Negate | Not
|
|
||||||
|
|
||||||
type expression =
|
|
||||||
| Literal of literal
|
|
||||||
| Identifier of string
|
|
||||||
| UnaryExpression of unary_operator * expression
|
|
||||||
| BinaryExpression of binary_operator * expression * expression
|
|
||||||
|
|
||||||
type variable_declaration =
|
|
||||||
| VariableDeclaration of _type * expression * expression
|
|
||||||
|
|
||||||
type statement =
|
|
||||||
| ExpressionStatement of expression
|
|
||||||
| VariableStatement of variable_declaration list
|
|
||||||
|
|
||||||
type source_file = SourceFile of statement list
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
(* lib/ast/syntax.ml *)
|
|
||||||
|
|
||||||
open Syntax
|
|
||||||
|
|
||||||
exception Type_error of string
|
|
||||||
|
|
||||||
type environment = { variables : (string * _type) list }
|
|
||||||
|
|
||||||
(** [type_of_literal lit] returns the type of the given literal. *)
|
|
||||||
let type_of_literal (lit : literal) : _type =
|
|
||||||
match lit with
|
|
||||||
| Integer _ -> IntegerType
|
|
||||||
| Float _ -> FloatType
|
|
||||||
| Character _ -> CharacterType
|
|
||||||
| String _ -> StringType
|
|
||||||
| Boolean _ -> BooleanType
|
|
||||||
| Null -> VoidType
|
|
||||||
|
|
||||||
(** [type_of_expression env expr] returns the type of the given expression. *)
|
|
||||||
let rec type_of_expression (env : environment) (expr : expression) : _type =
|
|
||||||
match expr with
|
|
||||||
| Literal lit -> type_of_literal lit
|
|
||||||
| Identifier id -> (
|
|
||||||
try List.assoc id env.variables
|
|
||||||
with Not_found -> raise (Type_error ("Unknown variable " ^ id)))
|
|
||||||
| UnaryExpression (op, expr) -> (
|
|
||||||
let expr_type = type_of_expression env expr in
|
|
||||||
match op with
|
|
||||||
| Negate -> (
|
|
||||||
match expr_type with
|
|
||||||
| IntegerType | FloatType -> expr_type
|
|
||||||
| _ ->
|
|
||||||
raise
|
|
||||||
(Type_error "Negate operator can only be applied to numbers"))
|
|
||||||
| Not -> (
|
|
||||||
match expr_type with
|
|
||||||
| BooleanType -> BooleanType
|
|
||||||
| _ ->
|
|
||||||
raise (Type_error "Not operator can only be applied to booleans"))
|
|
||||||
)
|
|
||||||
| BinaryExpression (op, left, right) -> (
|
|
||||||
let left_type = type_of_expression env left in
|
|
||||||
let right_type = type_of_expression env right in
|
|
||||||
match op with
|
|
||||||
| Add | Subtract | Multiply | Divide -> (
|
|
||||||
match (left_type, right_type) with
|
|
||||||
| IntegerType, IntegerType -> IntegerType
|
|
||||||
| FloatType, FloatType -> FloatType
|
|
||||||
| IntegerType, FloatType | FloatType, IntegerType -> FloatType
|
|
||||||
| _ -> raise (Type_error "Arithmetic operations require numbers"))
|
|
||||||
| AmpersandAmpersand | BarBar -> (
|
|
||||||
match (left_type, right_type) with
|
|
||||||
| BooleanType, BooleanType -> BooleanType
|
|
||||||
| _ -> raise (Type_error "Logical operations require booleans"))
|
|
||||||
| EqualsEquals | ExclamationEquals -> BooleanType
|
|
||||||
| LessThan | LessThanEquals | GreaterThan | GreaterThanEquals -> (
|
|
||||||
match (left_type, right_type) with
|
|
||||||
| IntegerType, IntegerType | FloatType, FloatType -> BooleanType
|
|
||||||
| IntegerType, FloatType | FloatType, IntegerType -> BooleanType
|
|
||||||
| _ -> raise (Type_error "Comparison operations require numbers"))
|
|
||||||
| Assign -> (
|
|
||||||
match left with
|
|
||||||
| Identifier _ ->
|
|
||||||
if left_type = right_type then left_type
|
|
||||||
else raise (Type_error "Assignment requires matching types")
|
|
||||||
| _ ->
|
|
||||||
raise
|
|
||||||
(Type_error
|
|
||||||
"Assignment requires a variable on the left-hand side")))
|
|
||||||
|
|
||||||
(** [type_of_variable_declaration env var_decl] returns the environment after
|
|
||||||
processing the given variable declaration. *)
|
|
||||||
let type_of_variable_declaration (env : environment)
|
|
||||||
(VariableDeclaration (_type, id_expr, expr)) =
|
|
||||||
match id_expr with
|
|
||||||
| Identifier id ->
|
|
||||||
let expr_type = type_of_expression env expr in
|
|
||||||
if expr_type = _type then { variables = (id, _type) :: env.variables }
|
|
||||||
else
|
|
||||||
raise (Type_error ("Type mismatch in variable declaration for " ^ id))
|
|
||||||
| _ -> raise (Type_error "Variable name must be an identifier")
|
|
||||||
|
|
||||||
(** [type_of_statement env stmt] returns the environment after processing the
|
|
||||||
given statement. *)
|
|
||||||
let type_of_statement (env : environment) (stmt : statement) =
|
|
||||||
match stmt with
|
|
||||||
| ExpressionStatement expr ->
|
|
||||||
let _ = type_of_expression env expr in
|
|
||||||
env
|
|
||||||
| VariableStatement var_decls ->
|
|
||||||
List.fold_left type_of_variable_declaration env var_decls
|
|
||||||
|
|
||||||
(** [type_of_source_file (SourceFile stmts)] returns the environment after
|
|
||||||
processing the given source file. *)
|
|
||||||
let type_of_source_file (SourceFile stmts) =
|
|
||||||
let initial_env = { variables = [] } in
|
|
||||||
List.fold_left type_of_statement initial_env stmts
|
|
||||||
@@ -1 +1,3 @@
|
|||||||
;lib/dune
|
(library
|
||||||
|
(name Croissant)
|
||||||
|
(libraries Syntax Analyzer))
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
; lib/eval/dune
|
|
||||||
|
|
||||||
;(library
|
|
||||||
; (name eval)
|
|
||||||
; (modules eval))
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
type binary_operators = Add | Subtract | Multiply | Divide
|
||||||
|
|
||||||
|
let pp_binary_operators = function
|
||||||
|
| Add -> "Add"
|
||||||
|
| Subtract -> "Subtract"
|
||||||
|
| Multiply -> "Multiply"
|
||||||
|
| Divide -> "Divide"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
open Literals
|
||||||
|
open UnaryOperators
|
||||||
|
open BinaryOperators
|
||||||
|
|
||||||
|
type expressions =
|
||||||
|
| Literal of literals
|
||||||
|
| UnaryExpression of unary_operators * expressions
|
||||||
|
| BinaryExpression of binary_operators * expressions * expressions
|
||||||
|
|
||||||
|
let rec pp_expressions = function
|
||||||
|
| Literal l ->
|
||||||
|
let pp_l = pp_literals l in
|
||||||
|
Printf.sprintf "Literal(%s)" pp_l
|
||||||
|
| UnaryExpression (op, e) ->
|
||||||
|
let pp_op = pp_unary_operators op and pp_e = pp_expressions e in
|
||||||
|
Printf.sprintf "UnaryExpression(%s, %s)" pp_op pp_e
|
||||||
|
| BinaryExpression (op, e1, e2) ->
|
||||||
|
let pp_op = pp_binary_operators op
|
||||||
|
and pp_e1 = pp_expressions e1
|
||||||
|
and pp_e2 = pp_expressions e2 in
|
||||||
|
Printf.sprintf "BinaryExpression(%s, %s, %s)" pp_op pp_e1 pp_e2
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
type literals =
|
||||||
|
| Integer of int
|
||||||
|
| Float of float
|
||||||
|
| Character of char
|
||||||
|
| String of string
|
||||||
|
| Boolean of bool
|
||||||
|
| Null
|
||||||
|
|
||||||
|
let pp_literals = function
|
||||||
|
| Integer i -> Printf.sprintf "Integer(%d)" i
|
||||||
|
| Float f -> Printf.sprintf "Float(%f)" f
|
||||||
|
| Character c -> Printf.sprintf "Character('%c')" c
|
||||||
|
| String s -> Printf.sprintf "String(\"%s\")" s
|
||||||
|
| Boolean b -> Printf.sprintf "Boolean(%b)" b
|
||||||
|
| Null -> "Null"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
open Statements
|
||||||
|
|
||||||
|
type source_files = SourceFile of statements
|
||||||
|
|
||||||
|
let pp_source_files = function
|
||||||
|
| SourceFile stmts ->
|
||||||
|
let pp_stmt = pp_statements stmts in
|
||||||
|
Printf.sprintf "SourceFile(%s)" pp_stmt
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
open Expressions
|
||||||
|
|
||||||
|
type statements =
|
||||||
|
| SequenceStatement of statements * statements
|
||||||
|
| ExpressionStatement of expressions
|
||||||
|
|
||||||
|
let rec pp_statements = function
|
||||||
|
| SequenceStatement (stmt1, stmt2) ->
|
||||||
|
let pp_stmt1 = pp_statements stmt1 and pp_stmt2 = pp_statements stmt2 in
|
||||||
|
Printf.sprintf "SequenceStatement(%s, %s)" pp_stmt1 pp_stmt2
|
||||||
|
| ExpressionStatement expr ->
|
||||||
|
let pp_expr = pp_expressions expr in
|
||||||
|
Printf.sprintf "ExpressionStatement(%s)" pp_expr
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
type types =
|
||||||
|
| IntegerType
|
||||||
|
| FloatType
|
||||||
|
| CharacterType
|
||||||
|
| StringType
|
||||||
|
| BooleanType
|
||||||
|
| VoidType
|
||||||
|
|
||||||
|
let pp_types = function
|
||||||
|
| IntegerType -> "IntegerType"
|
||||||
|
| FloatType -> "FloatType"
|
||||||
|
| CharacterType -> "CharacterType"
|
||||||
|
| StringType -> "StringType"
|
||||||
|
| BooleanType -> "BooleanType"
|
||||||
|
| VoidType -> "VoidType"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
type unary_operators = ArithmeticNegation | LogicalNegation
|
||||||
|
|
||||||
|
let pp_unary_operators = function
|
||||||
|
| ArithmeticNegation -> "ArithmeticNegation"
|
||||||
|
| LogicalNegation -> "LogicalNegation"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
(library
|
||||||
|
(name Syntax)
|
||||||
|
(modules
|
||||||
|
Types
|
||||||
|
Literals
|
||||||
|
BinaryOperators
|
||||||
|
UnaryOperators
|
||||||
|
Expressions
|
||||||
|
Statements
|
||||||
|
SourceFiles))
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
;test/ast/dune
|
|
||||||
|
|
||||||
(test
|
|
||||||
(name print)
|
|
||||||
(libraries alcotest ast)
|
|
||||||
)
|
|
||||||
|
|
||||||
(test
|
|
||||||
(name typecheck)
|
|
||||||
(libraries alcotest ast)
|
|
||||||
)
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
(* test/ast/print.ml *)
|
|
||||||
|
|
||||||
open Alcotest
|
|
||||||
open Ast.Syntax
|
|
||||||
|
|
||||||
module To_test = struct
|
|
||||||
let _type = Ast.Print.string_of_type
|
|
||||||
let literal = Ast.Print.string_of_literal
|
|
||||||
let binary_operator = Ast.Print.string_of_binary_operator
|
|
||||||
let unary_operator = Ast.Print.string_of_unary_operator
|
|
||||||
let expression = Ast.Print.string_of_expression
|
|
||||||
let variable_declaration = Ast.Print.string_of_variable_declaration
|
|
||||||
let statement = Ast.Print.string_of_statement
|
|
||||||
let source_file = Ast.Print.string_of_source_file
|
|
||||||
end
|
|
||||||
|
|
||||||
let test_string_of_type () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("int", "IntegerType", IntegerType);
|
|
||||||
("float", "FloatType", FloatType);
|
|
||||||
("char", "CharacterType", CharacterType);
|
|
||||||
("string", "StringType", StringType);
|
|
||||||
("bool", "BooleanType", BooleanType);
|
|
||||||
("void", "VoidType", VoidType);
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test._type actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_literal () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("42", "Integer(42)", Integer 42);
|
|
||||||
("3.14", "Float(3.14)", Float 3.14);
|
|
||||||
("true", "Boolean(true)", Boolean true);
|
|
||||||
("false", "Boolean(false)", Boolean false);
|
|
||||||
("hello", "String(\"hello\")", String "hello");
|
|
||||||
("c", "Character('c')", Character 'c');
|
|
||||||
("null", "Null", Null);
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.literal actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_binary_operator () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("+", "Add", Add);
|
|
||||||
("-", "Subtract", Subtract);
|
|
||||||
("*", "Multiply", Multiply);
|
|
||||||
("/", "Divide", Divide);
|
|
||||||
("&&", "AmpersandAmpersand", AmpersandAmpersand);
|
|
||||||
("||", "BarBar", BarBar);
|
|
||||||
("==", "EqualsEquals", EqualsEquals);
|
|
||||||
("!=", "ExclamationEquals", ExclamationEquals);
|
|
||||||
("<", "LessThan", LessThan);
|
|
||||||
("<=", "LessThanEquals", LessThanEquals);
|
|
||||||
(">", "GreaterThan", GreaterThan);
|
|
||||||
(">=", "GreaterThanEquals", GreaterThanEquals);
|
|
||||||
("=", "Assign", Assign);
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.binary_operator actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_unary_operator () =
|
|
||||||
let tests = [ ("-", "Negate", Negate); ("!", "Not", Not) ] in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.unary_operator actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_expression () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("42", "Literal(Integer(42))", Literal (Integer 42));
|
|
||||||
( "1 + 2",
|
|
||||||
"BinaryExpression(Add, Literal(Integer(1)), Literal(Integer(2)))",
|
|
||||||
BinaryExpression (Add, Literal (Integer 1), Literal (Integer 2)) );
|
|
||||||
( "-42",
|
|
||||||
"UnaryExpression(Negate, Literal(Integer(42)))",
|
|
||||||
UnaryExpression (Negate, Literal (Integer 42)) );
|
|
||||||
("x", "Identifier(\"x\")", Identifier "x");
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.expression actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_variable_declaration () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
( "var x: int = 42;",
|
|
||||||
"VariableDeclaration(IntegerType, Identifier(\"x\"), \
|
|
||||||
Literal(Integer(42)))",
|
|
||||||
VariableDeclaration (IntegerType, Identifier "x", Literal (Integer 42))
|
|
||||||
);
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.variable_declaration actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_statement () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
( "42;",
|
|
||||||
"ExpressionStatement(Literal(Integer(42)))",
|
|
||||||
ExpressionStatement (Literal (Integer 42)) );
|
|
||||||
( "var x: int;",
|
|
||||||
"VariableStatement([VariableDeclaration(IntegerType, \
|
|
||||||
Identifier(\"x\"), Literal(Null))])",
|
|
||||||
VariableStatement
|
|
||||||
[ VariableDeclaration (IntegerType, Identifier "x", Literal Null) ] );
|
|
||||||
( "var x: int = 42;",
|
|
||||||
"VariableStatement([VariableDeclaration(IntegerType, \
|
|
||||||
Identifier(\"x\"), Literal(Integer(42)))])",
|
|
||||||
VariableStatement
|
|
||||||
[
|
|
||||||
VariableDeclaration
|
|
||||||
(IntegerType, Identifier "x", Literal (Integer 42));
|
|
||||||
] );
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.statement actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_string_of_source_file () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
( "1; 2 + 3;",
|
|
||||||
"SourceFile([ExpressionStatement(Literal(Integer(1))), \
|
|
||||||
ExpressionStatement(BinaryExpression(Add, Literal(Integer(2)), \
|
|
||||||
Literal(Integer(3))))])",
|
|
||||||
SourceFile
|
|
||||||
[
|
|
||||||
ExpressionStatement (Literal (Integer 1));
|
|
||||||
ExpressionStatement
|
|
||||||
(BinaryExpression (Add, Literal (Integer 2), Literal (Integer 3)));
|
|
||||||
] );
|
|
||||||
]
|
|
||||||
in
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, actual) ->
|
|
||||||
(check string) name expected (To_test.source_file actual))
|
|
||||||
tests
|
|
||||||
|
|
||||||
let () =
|
|
||||||
run "ast.print"
|
|
||||||
[
|
|
||||||
("string_of_type", [ test_case "type" `Quick test_string_of_type ]);
|
|
||||||
( "string_of_literal",
|
|
||||||
[ test_case "literal" `Quick test_string_of_literal ] );
|
|
||||||
( "string_of_unary_operator",
|
|
||||||
[ test_case "unary_operator" `Quick test_string_of_unary_operator ] );
|
|
||||||
( "string_of_binary_operator",
|
|
||||||
[ test_case "binary_operator" `Quick test_string_of_binary_operator ] );
|
|
||||||
( "string_of_expression",
|
|
||||||
[ test_case "expression" `Quick test_string_of_expression ] );
|
|
||||||
( "string_of_variable_declaration",
|
|
||||||
[
|
|
||||||
test_case "variable_declaration" `Quick
|
|
||||||
test_string_of_variable_declaration;
|
|
||||||
] );
|
|
||||||
( "string_of_statement",
|
|
||||||
[ test_case "statement" `Quick test_string_of_statement ] );
|
|
||||||
( "string_of_source_file",
|
|
||||||
[ test_case "source_file" `Quick test_string_of_source_file ] );
|
|
||||||
]
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
(* test/ast/typecheck.ml *)
|
|
||||||
|
|
||||||
open Alcotest
|
|
||||||
open Ast.Syntax
|
|
||||||
open Ast.Typecheck
|
|
||||||
|
|
||||||
let should_be_valid function_to_test tests =
|
|
||||||
List.iter
|
|
||||||
(fun (name, expected, source) ->
|
|
||||||
let actual = function_to_test source in
|
|
||||||
check (of_pp Fmt.nop) name expected actual)
|
|
||||||
tests
|
|
||||||
|
|
||||||
let test_type_of_literal () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("1", IntegerType, Integer 1);
|
|
||||||
("1.0", FloatType, Float 1.0);
|
|
||||||
("true", BooleanType, Boolean true);
|
|
||||||
("false", BooleanType, Boolean false);
|
|
||||||
("\"hello\"", StringType, String "hello");
|
|
||||||
("'c'", CharacterType, Character 'c');
|
|
||||||
("null", VoidType, Null);
|
|
||||||
]
|
|
||||||
in
|
|
||||||
should_be_valid type_of_literal tests
|
|
||||||
|
|
||||||
let test_type_of_expression () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
("42", IntegerType, Literal (Integer 42));
|
|
||||||
("foo", StringType, Identifier "foo");
|
|
||||||
(* TODO Add tests *)
|
|
||||||
]
|
|
||||||
and env : environment = { variables = [ ("foo", StringType) ] } in
|
|
||||||
should_be_valid (type_of_expression env) tests
|
|
||||||
(* TODO Tests errors *)
|
|
||||||
|
|
||||||
let test_type_of_variable_declaration () =
|
|
||||||
let tests =
|
|
||||||
[
|
|
||||||
( "var bar: booleen = true;",
|
|
||||||
{ variables = [ ("bar", BooleanType) ] },
|
|
||||||
VariableDeclaration
|
|
||||||
(BooleanType, Identifier "bar", Literal (Boolean true)) );
|
|
||||||
]
|
|
||||||
and env : environment = { variables = [] } in
|
|
||||||
should_be_valid (type_of_variable_declaration env) tests
|
|
||||||
(* TODO check other case should return an error *)
|
|
||||||
|
|
||||||
let test_type_of_statement () =
|
|
||||||
let tests = [ (* TODO Add Tests *) ] and env = { variables = [] } in
|
|
||||||
should_be_valid (type_of_statement env) tests
|
|
||||||
|
|
||||||
let test_type_of_source_file () =
|
|
||||||
let tests = [ (* TODO Add Tests *) ] in
|
|
||||||
should_be_valid type_of_source_file tests
|
|
||||||
|
|
||||||
let () =
|
|
||||||
run "ast.typecheck"
|
|
||||||
[
|
|
||||||
( "type_of_literal",
|
|
||||||
[ test_case "type_of_literal" `Quick test_type_of_literal ] );
|
|
||||||
( "type_of_expression",
|
|
||||||
[ test_case "type_of_expression" `Quick test_type_of_expression ] );
|
|
||||||
( "type_of_variable_declaration",
|
|
||||||
[
|
|
||||||
test_case "type_of_variable_declaration" `Quick
|
|
||||||
test_type_of_variable_declaration;
|
|
||||||
] );
|
|
||||||
( "type_of_statement",
|
|
||||||
[ test_case "type_of_statement" `Quick test_type_of_statement ] );
|
|
||||||
( "type_of_source_file",
|
|
||||||
[ test_case "type_of_source_file" `Quick test_type_of_source_file ] );
|
|
||||||
]
|
|
||||||
+33
-1
@@ -1 +1,33 @@
|
|||||||
var a : caractere = '';
|
1 + 1;
|
||||||
|
4 * 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Root(
|
||||||
|
SequenceStatement(
|
||||||
|
ExpressionStatement(
|
||||||
|
BinaryExpression(
|
||||||
|
Add,
|
||||||
|
Literal(
|
||||||
|
Integer(1)
|
||||||
|
),
|
||||||
|
Literal(
|
||||||
|
Integer(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ExpressionStatement(
|
||||||
|
BinaryExpression(
|
||||||
|
Multiply,
|
||||||
|
Literal(
|
||||||
|
Integer(4)
|
||||||
|
),
|
||||||
|
Literal(
|
||||||
|
Integer(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax.BinaryOperators
|
||||||
|
|
||||||
|
let test_pp_binary_operators () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
("Should return \"Add\"", "Add", Add);
|
||||||
|
("Should return \"Subtract\"", "Subtract", Subtract);
|
||||||
|
("Should return \"Multiply\"", "Multiply", Multiply);
|
||||||
|
("Should return \"Divide\"", "Divide", Divide);
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (msg, expected, actual) ->
|
||||||
|
check string msg expected (pp_binary_operators actual))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
( "BinaryOperators",
|
||||||
|
[ test_case "pp_binary_operators" `Quick test_pp_binary_operators ] )
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax
|
||||||
|
|
||||||
|
let test_pp_expressions () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
( "Should return \"Literal(...)\"",
|
||||||
|
"Literal(Integer(1))",
|
||||||
|
Expressions.Literal (Literals.Integer 1) );
|
||||||
|
( "Should return \"UnaryExpression(...)\"",
|
||||||
|
"UnaryExpression(ArithmeticNegation, Literal(Integer(2)))",
|
||||||
|
Expressions.UnaryExpression
|
||||||
|
( UnaryOperators.ArithmeticNegation,
|
||||||
|
Expressions.Literal (Literals.Integer 2) ) );
|
||||||
|
( "Should return \"BinaryExpression(...)\"",
|
||||||
|
"BinaryExpression(Add, Literal(Integer(5)), Literal(Integer(10)))",
|
||||||
|
Expressions.BinaryExpression
|
||||||
|
( BinaryOperators.Add,
|
||||||
|
Expressions.Literal (Literals.Integer 5),
|
||||||
|
Expressions.Literal (Literals.Integer 10) ) );
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (msg, expected, actual) ->
|
||||||
|
check string msg expected (Expressions.pp_expressions actual))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
("Expressions", [ test_case "pp_expressions" `Quick test_pp_expressions ])
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax.Literals
|
||||||
|
|
||||||
|
let test_pp_literals () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
("Should return \"Integer(1)\"", "Integer(1)", Integer 1);
|
||||||
|
("Should return \"Float(1.000000)\"", "Float(1.000000)", Float 1.0);
|
||||||
|
( "Should return \"String(\\\"Hello, World!\\\")\"",
|
||||||
|
"String(\"Hello, World!\")",
|
||||||
|
String "Hello, World!" );
|
||||||
|
("Should return \"Character('c')\"", "Character('c')", Character 'c');
|
||||||
|
("Should return \"Boolean(true)\"", "Boolean(true)", Boolean true);
|
||||||
|
("Should return \"Boolean(false)\"", "Boolean(false)", Boolean false);
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (msg, expected, actual) ->
|
||||||
|
check string msg expected (pp_literals actual))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests = ("Literals", [ test_case "pp_literals" `Quick test_pp_literals ])
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax
|
||||||
|
|
||||||
|
let test_pp_source_files () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
( "Should return \"SourceFile(...)\"",
|
||||||
|
"SourceFile(ExpressionStatement(BinaryExpression(Add, \
|
||||||
|
Literal(Integer(1)), Literal(Integer(2)))))",
|
||||||
|
SourceFiles.SourceFile
|
||||||
|
(Statements.ExpressionStatement
|
||||||
|
(Expressions.BinaryExpression
|
||||||
|
( BinaryOperators.Add,
|
||||||
|
Expressions.Literal (Literals.Integer 1),
|
||||||
|
Expressions.Literal (Literals.Integer 2) ))) );
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (name, expected, actual) ->
|
||||||
|
check string name expected (SourceFiles.pp_source_files actual))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
("SourceFiles", [ test_case "pp_source_files" `Quick test_pp_source_files ])
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax
|
||||||
|
|
||||||
|
let test_pp_statements () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
( "Should return \"ExpressionStatement(...)\"",
|
||||||
|
"ExpressionStatement(Literal(Integer(1)))",
|
||||||
|
Statements.ExpressionStatement
|
||||||
|
(Expressions.Literal (Literals.Integer 1)) );
|
||||||
|
( "Should return \"SequenceStatement(...)\"",
|
||||||
|
"SequenceStatement(ExpressionStatement(Literal(Integer(1))), \
|
||||||
|
ExpressionStatement(Literal(Float(1.000000))))",
|
||||||
|
Statements.SequenceStatement
|
||||||
|
( Statements.ExpressionStatement
|
||||||
|
(Expressions.Literal (Literals.Integer 1)),
|
||||||
|
Statements.ExpressionStatement
|
||||||
|
(Expressions.Literal (Literals.Float 1.0)) ) );
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (name, expected, actual) ->
|
||||||
|
check string name expected (Statements.pp_statements actual))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
("Statements", [ test_case "pp_statements" `Quick test_pp_statements ])
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
open Alcotest
|
||||||
|
|
||||||
|
let () =
|
||||||
|
run "Syntax"
|
||||||
|
[
|
||||||
|
TestTypes.tests;
|
||||||
|
TestLiterals.tests;
|
||||||
|
TestUnaryOperators.tests;
|
||||||
|
TestBinaryOperators.tests;
|
||||||
|
TestExpressions.tests;
|
||||||
|
TestStatements.tests;
|
||||||
|
TestSourceFiles.tests;
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax.Types
|
||||||
|
|
||||||
|
let test_pp_types () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
("Should return \"IntegerType\"", "IntegerType", IntegerType);
|
||||||
|
("Should return \"BooleanType\"", "BooleanType", BooleanType);
|
||||||
|
("Should return \"StringType\"", "StringType", StringType);
|
||||||
|
("Should return \"CharacterType\"", "CharacterType", CharacterType);
|
||||||
|
("Should return \"VoidType\"", "VoidType", VoidType);
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (msg, expected, input) -> check string msg expected (pp_types input))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests = ("Types", [ test_case "pp_types" `Quick test_pp_types ])
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
open Alcotest
|
||||||
|
open Syntax.UnaryOperators
|
||||||
|
|
||||||
|
let test_pp_unary_operators () =
|
||||||
|
let to_check =
|
||||||
|
[
|
||||||
|
( "Should return \"ArithmeticNegation\"",
|
||||||
|
ArithmeticNegation,
|
||||||
|
"ArithmeticNegation" );
|
||||||
|
]
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (msg, unary_operator, expected) ->
|
||||||
|
check string msg expected (pp_unary_operators unary_operator))
|
||||||
|
to_check
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
( "UnaryOperators",
|
||||||
|
[ test_case "pp_unary_operators" `Quick test_pp_unary_operators ] )
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
(test
|
||||||
|
(name TestSyntax)
|
||||||
|
(libraries Syntax alcotest)
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user