From 0321df3708cfa4d1440faf3f407611df85484b4b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 16 Mar 2025 02:00:40 +0530 Subject: Refactor files to cljcc-compiler and cli tool. --- cljcc-compiler/src/cljcc/analyze/resolve.clj | 300 +++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 cljcc-compiler/src/cljcc/analyze/resolve.clj (limited to 'cljcc-compiler/src/cljcc/analyze/resolve.clj') diff --git a/cljcc-compiler/src/cljcc/analyze/resolve.clj b/cljcc-compiler/src/cljcc/analyze/resolve.clj new file mode 100644 index 0000000..9f09333 --- /dev/null +++ b/cljcc-compiler/src/cljcc/analyze/resolve.clj @@ -0,0 +1,300 @@ +(ns cljcc.analyze.resolve + (:require [cljcc.exception :as exc] + [cljcc.parser :as p] + [malli.dev.pretty :as pretty] + [cljcc.schema :as s] + [cljcc.util :as util] + [malli.core :as m])) + +(defn- unique-identifier [identifier] + (util/create-identifier! identifier)) + +(defn- copy-identifier-map + "Returns a copy of the identifier -> symbol map. + + Sets :at-top-level false, as it's going inside a scope. ( Could be fn definition, compound statement ). + Sets :from-current-scope as false for every symbol. Used when going into a inner scope." + [ident->symbol] + (let [set-from-current-scope-as-false (fn [i->s] + (zipmap (keys i->s) + (map (fn [s] + (assoc s :from-current-scope false)) + (vals i->s))))] + (-> ident->symbol + (dissoc :at-top-level) + set-from-current-scope-as-false + (assoc :at-top-level false)))) + +(declare resolve-block resolve-declaration resolve-optional-exp) + +(defn- resolve-exp [e ident->symbol] + (condp = (:exp-type e) + :constant-exp e + :variable-exp (if (contains? ident->symbol (:identifier e)) + (p/variable-exp-node (:name (get ident->symbol (:identifier e)))) + (exc/analyzer-error "Undeclared variable seen." {:variable e})) + :assignment-exp (let [left (:left e) + right (:right e) + op (:assignment-operator e) + left-var? (= :variable-exp (:exp-type left))] + (if left-var? + (p/assignment-exp-node (resolve-exp left ident->symbol) + (resolve-exp right ident->symbol) + op) + (exc/analyzer-error "Invalid lvalue in assignment expression." {:lvalue e}))) + :binary-exp (p/binary-exp-node (resolve-exp (:left e) ident->symbol) + (resolve-exp (:right e) ident->symbol) + (:binary-operator e)) + :unary-exp (p/unary-exp-node (:unary-operator e) (resolve-exp (:value e) ident->symbol)) + :conditional-exp (p/conditional-exp-node (resolve-exp (:left e) ident->symbol) + (resolve-exp (:middle e) ident->symbol) + (resolve-exp (:right e) ident->symbol)) + :cast-exp (p/cast-exp-node (:target-type e) + (resolve-exp (:value e) ident->symbol)) + :function-call-exp (let [fn-name (:identifier e) + args (:arguments e)] + (if (contains? ident->symbol fn-name) + (p/function-call-exp-node (:new-name (get ident->symbol fn-name)) + (mapv #(resolve-exp % ident->symbol) args)) + (exc/analyzer-error "Undeclared function." {:function-name fn-name}))) + (exc/analyzer-error "Invalid expression." {:exp e}))) + +(defn- resolve-optional-exp [e ident->symbol] + (if (nil? e) + e + (resolve-exp e ident->symbol))) + +(defn- resolve-file-scope-variable-declaration + "Adds file scope variable declaration to scope. + + Directly adds variable declaration to map as it is top level." + [{:keys [identifier] :as declaration} ident->symbol] + {:declaration declaration + :ident->symbol (assoc ident->symbol identifier {:new-name identifier + :name identifier + :from-current-scope true + :has-linkage true})}) + +(defn- resolve-local-variable-declaration + "Add local variable declaration. + + Validates for variables declared with same name. + Validates for variables declared from different scope, but with conflicting storage class." + [{:keys [identifier initial variable-type storage-class] :as declaration} ident->symbol] + (let [prev-entry (get ident->symbol identifier) + extern? (= storage-class :extern) + _ (when (and prev-entry (:from-current-scope prev-entry)) + (when (not (and (:has-linkage prev-entry) extern?)) + (exc/analyzer-error "Conflicting local declaration." {:declaration declaration})))] + (if extern? + {:declaration declaration + :ident->symbol (assoc ident->symbol identifier {:new-name identifier + :name identifier + :from-current-scope true + :has-linkage true})} + (let [unique-name (unique-identifier identifier) + updated-symbols (assoc ident->symbol identifier {:new-name unique-name + :name unique-name + :from-current-scope true + :has-linkage false}) + init-value (when initial (resolve-exp initial updated-symbols))] + {:declaration (p/variable-declaration-node unique-name storage-class variable-type init-value) + :ident->symbol updated-symbols})))) + +(defn- resolve-variable-declaration + "Resolves variable declarations. + + Ensures variable not declared twice in the current scope." + [decl {:keys [at-top-level] :as ident->symbol}] + (if at-top-level + (resolve-file-scope-variable-declaration decl ident->symbol) + (resolve-local-variable-declaration decl ident->symbol))) + +(defn- resolve-parameter [parameter ident->symbol] + (if (and (contains? ident->symbol parameter) + (:from-current-scope (get ident->symbol parameter))) + (exc/analyzer-error "Parameter name duplicated." {:parameter parameter}) + (let [unique-name (unique-identifier parameter) + updated-identifier-map (assoc ident->symbol parameter {:name unique-name + :from-current-scope true + :has-linkage false})] + {:parameter unique-name + :ident->symbol updated-identifier-map}))) + +(defn- resolve-parameters [params ident->symbol] + (reduce (fn [acc p] + (let [{:keys [parameter ident->symbol]} (resolve-parameter p (:ident->symbol acc))] + {:parameters (conj (:parameters acc) parameter) + :ident->symbol ident->symbol})) + {:parameters [] :ident->symbol ident->symbol} + params)) + +(defn- resolve-function-declaration + "Resolve function declaration. + + Ensures functions not declared twice in current scope with incorrect linkage." + [{:keys [identifier storage-class parameters function-type body] :as d} ident->symbol] + (let [prev-entry (get ident->symbol identifier) + already-declared-var? (and (contains? ident->symbol identifier) + (:from-current-scope (get ident->symbol identifier)) + (not (:has-linkage prev-entry))) + illegally-redeclared? (and (contains? ident->symbol identifier) + (:from-current-scope prev-entry) + (not (:has-linkage prev-entry))) + static? (= :static storage-class) + inside-function-definition? (not (:at-top-level ident->symbol)) + _ (when already-declared-var? + (exc/analyzer-error "Variable already declared in same scope." {:declaration d})) + _ (when illegally-redeclared? + (exc/analyzer-error "Function duplicate declaration." {:declaration d})) + updated-identifier-map (assoc ident->symbol identifier {:new-name identifier + :name identifier + :from-current-scope true + :has-linkage true}) + inner-map (copy-identifier-map updated-identifier-map) + {new-params :parameters, inner-map :ident->symbol} (resolve-parameters parameters inner-map) + _ (when (and body inside-function-definition?) + (exc/analyzer-error "Nested function definition not allowed." {:declaration d + :ident->symbol ident->symbol})) + _ (when (and inside-function-definition? static?) + (exc/analyzer-error "Nested static function declarations cannot exist." {:declaration d})) + new-body (when body (resolve-block body inner-map))] + {:declaration (p/function-declaration-node function-type storage-class identifier new-params (:block new-body)) + :ident->symbol updated-identifier-map})) + +(defn- resolve-declaration [{:keys [declaration-type] :as d} ident->symbol] + (condp = declaration-type + :variable (resolve-variable-declaration d ident->symbol) + :function (resolve-function-declaration d ident->symbol) + (exc/analyzer-error "Invalid declaration type" {:declaration d}))) + +(defn- resolve-for-init [for-init ident->symbol] + (if (= (:type for-init) :declaration) + (resolve-declaration for-init ident->symbol) + (resolve-optional-exp for-init ident->symbol))) + +(defmulti resolve-statement + "Resolves statements in a given scope. + + Scope here refers to the ident->symbol map, which holds declarations + visisble to statement at this time. + + Dispatches based on the type of statement. + + Returns statement after recursively resolving all expressions and statements. + " + (fn [statement _ident->symbol] + (:statement-type statement))) + +(defmethod resolve-statement :default [statement _] + (exc/analyzer-error "Invalid statement." {:statement statement})) + +(defmethod resolve-statement :return [{:keys [value]} ident->symbol] + (p/return-statement-node (resolve-exp value ident->symbol))) + +(defmethod resolve-statement :break [statement _] + statement) + +(defmethod resolve-statement :continue [statement _] + statement) + +(defmethod resolve-statement :empty [statement _] + statement) + +(defmethod resolve-statement :expression [{:keys [value]} ident->symbol] + (p/expression-statement-node (resolve-exp value ident->symbol))) + +(defmethod resolve-statement :if [{:keys [condition then-statement else-statement]} ident->symbol] + (if else-statement + (p/if-statement-node (resolve-exp condition ident->symbol) + (resolve-statement then-statement ident->symbol) + (resolve-statement else-statement ident->symbol)) + (p/if-statement-node (resolve-exp condition ident->symbol) + (resolve-statement then-statement ident->symbol)))) + +(defmethod resolve-statement :while [{:keys [condition body]} ident->symbol] + (p/while-statement-node (resolve-exp condition ident->symbol) + (resolve-statement body ident->symbol))) + +(defmethod resolve-statement :do-while [{:keys [condition body]} ident->symbol] + (p/do-while-statement-node (resolve-exp condition ident->symbol) + (resolve-statement body ident->symbol))) + +(defmethod resolve-statement :for [{:keys [init condition post body]} ident->symbol] + (let [for-scope-identifier-map (copy-identifier-map ident->symbol) + resolved-for-init (resolve-for-init init for-scope-identifier-map) + for-scope-identifier-map (if (:declaration resolved-for-init) ; updates symbol map if for initializer is declaration + (:ident->symbol resolved-for-init) + for-scope-identifier-map) + resolved-for-init (if (:declaration resolved-for-init) ; getting the underlying declaration, if it is + (:declaration resolved-for-init) + resolved-for-init) + condition (resolve-optional-exp condition for-scope-identifier-map) + post (resolve-optional-exp post for-scope-identifier-map) + body (resolve-statement body for-scope-identifier-map)] + (p/for-statement-node resolved-for-init condition post body))) + +(defmethod resolve-statement :compound [{:keys [block]} ident->symbol] + (p/compound-statement-node (:block (resolve-block block (copy-identifier-map ident->symbol))))) + +(defn- resolve-block-item [{:keys [type] :as item} ident->symbol] + (condp = type + :declaration (let [{d :declaration + i->s :ident->symbol} (resolve-declaration item ident->symbol)] + {:block-item d + :ident->symbol i->s}) + :statement {:block-item (resolve-statement item ident->symbol) + :ident->symbol ident->symbol})) + +(defn- resolve-block + "Resolves a block under a given symbol table. + + Block is list of block items. + + ident->symbol holds identifier to symbol mapping. + Symbol contains the type information, generated variable name etc. + + | key | description | + |----------------|-------------| + |`:at-top-level` | Is current level top or not ( default true)|" + ([block] + (resolve-block block {:at-top-level true})) + ([block ident->symbol] + (let [reduce-f (fn [acc block-item] + (let [res (resolve-block-item block-item (:ident->symbol acc))] + {:block (conj (:block acc) (:block-item res)) + :ident->symbol (:ident->symbol res)}))] + (reduce reduce-f + {:block [] + :ident->symbol ident->symbol} + block)))) + +;; Program is list of block items, which are themselves just blocks. +(defn resolve-program [program] + (let [res (:block (resolve-block program))] + ; _ (m/coerce s/Program res)] + res)) + +(comment + + (def file-path "./test-programs/example.c") + + (slurp "./test-programs/example.c") + + (-> file-path + slurp + p/parse-from-src) + + (-> file-path + slurp + p/parse-from-src + resolve-program) + + (pretty/explain + s/Program + (-> file-path + slurp + p/parse-from-src + resolve-program)) + + ()) -- cgit v1.2.3