mirror of
https://github.com/LucasVbr/croissant.git
synced 2026-07-09 15:07:48 +00:00
feat: Add typecheck to the AST
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
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
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
(library
|
||||
(name ast)
|
||||
(modules syntax print))
|
||||
(modules lexer parser syntax print typecheck analyzer)
|
||||
)
|
||||
|
||||
(menhir
|
||||
(modules parser))
|
||||
|
||||
(ocamllex
|
||||
(modules lexer))
|
||||
@@ -0,0 +1,70 @@
|
||||
(* lib/lexer.mll *)
|
||||
{
|
||||
open Parser
|
||||
exception Error of char
|
||||
}
|
||||
|
||||
let line_comment = "//" [^'.']*'\n'
|
||||
let block_comment = "/*" [^'.']* "*/"
|
||||
|
||||
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+
|
||||
let char = "'" [^'.'] "'"
|
||||
let string = '"' [^'.']* '"'
|
||||
|
||||
rule token = parse
|
||||
| ' ' | '\t' { token lexbuf }
|
||||
| '\n' | line_comment { Lexing.new_line lexbuf; token lexbuf }
|
||||
| block_comment as comment {
|
||||
String.iter (fun c -> if c = '\n' then Lexing.new_line lexbuf) comment;
|
||||
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) }
|
||||
| char as lxm { CHARACTER(lxm.[1]) }
|
||||
| string as lxm { STRING(String.sub lxm 1 (String.length lxm - 2)) }
|
||||
| identifier as lxm { IDENTIFIER(lxm) }
|
||||
|
||||
| eof { EOF }
|
||||
| _ as c { raise (Error c) }
|
||||
@@ -0,0 +1,117 @@
|
||||
/* 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) }
|
||||
+10
-10
@@ -11,10 +11,19 @@ let string_of_type = function
|
||||
| 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(\"" ^ 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"
|
||||
| Substract -> "Substract"
|
||||
| Subtract -> "Subtract"
|
||||
| Multiply -> "Multiply"
|
||||
| Divide -> "Divide"
|
||||
| AmpersandAmpersand -> "AmpersandAmpersand"
|
||||
@@ -30,15 +39,6 @@ let string_of_binary_operator = function
|
||||
(** [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_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(\"" ^ s ^ "\")"
|
||||
| Boolean b -> "Boolean(" ^ string_of_bool b ^ ")"
|
||||
| Null -> "Null"
|
||||
|
||||
(** [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 ^ ")"
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ type literal =
|
||||
|
||||
type binary_operator =
|
||||
| Add
|
||||
| Substract
|
||||
| Subtract
|
||||
| Multiply
|
||||
| Divide
|
||||
| AmpersandAmpersand
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
(* 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
|
||||
Reference in New Issue
Block a user