From 1d851522ba7248fc1e90fe5aa8f14cdc20f138d4 Mon Sep 17 00:00:00 2001 From: Tan Kian-ting Date: Sun, 7 Sep 2025 22:48:50 +0800 Subject: [PATCH] add parser rules and add basic typechecker (not tested) --- src/TComp.jl | 77 ++++++++++++++++++++++++++++ src/parser.jl | 137 ++++++++++++++++++++++++++++++++++++++++++++++---- test/prog3.tc | 8 +++ 3 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 test/prog3.tc diff --git a/src/TComp.jl b/src/TComp.jl index 23aa43c..43a1c5e 100644 --- a/src/TComp.jl +++ b/src/TComp.jl @@ -21,6 +21,83 @@ println("PARSED\n", Parser.prettyStringLisp(parsed)) tmp_var_no = 0 +typeEnv = [ + ("+", ("Fn", ["int", "int"], "int")), + ("-", ("Fn", ["int", "int"], "int")), + ("*", ("Fn", ["int", "int"], "int")), + ("/", ("Fn", ["int", "int"], "int")), + ] +function typeCheck(ast, typeEnv) + pl = Parser.prettyStringLisp + result = @match ast begin + (_, "bool") => "bool" + (_, "int") => "int" + [("%prime", _), (op, _), (lhs, rhs)] => begin + t_lhs = typeCheck(lhs, env) + t_rhs = typeCheck(rhs, env) + actualInputType = [t_lhs, t_rhs] + funcType = findfirst(x->x[1] == op, typeEnv)[2] + expectedInputType = funcType[2] + zipped = zip(expectedInputType, actualInputType) + mapped = map(a -> a[1] && a[2] , zipped) + reduced = reduce((x,y)->x || y, mapped) + if reduced == true + return funcType[3] + else + throw("Type Error: Operands of" + * op * " expected to be" + * string(expectedInputType) + * ", found" + * string(actualInputType)) + end + end + (var, "%id") => begin + t_var_id = findfirst(x->x[1] == var, typeEnv) + if t_var_id == nothing + throw(var * "not found in typeEnv") + else + return typeEnv[t_var_id] + end + end + [("%let", _),[(tVar, _), (var_)], exp, body] => + begin + typeOfExp = typeCheck(exp, typeEnv) + if tVar != typeOfExp + throw("Type Error: type of " * var + * "should be" * tVar + * ", found " * typeOfExp) + else + newTypeEnv = typeEnv + pushfirst!((var, tVar), newTypeEnv) + return typeCheck(body, newTypeEnv) + end + end + [("!", "not"), operand] => + begin + tOperand = typeCheck(operand, typeEnv) + if tOperand != "bool" + throw("Type Error: type of operands for !" + * "should be" * tVar + * ", found " * typeOfExp) + else + return "bool" + end + end + [(cmp, _), lhs, rhs] where (cmp[2]) in ["==", "!=", "<", "<=", ">", ">=", "&", "|"] => + begin + tLhs = typeCheck(lhs, typeEnv) + tRhs = typeCheck(rhs, typeEnv) + if !(tLhs == "bool" & tRhs == "bool") + throw("Type Error: operands of" + * cmp * "should be (bool, bool), not" + * string([tLhs, tRhs])) + else + return "bool" + end + end + end + +end # Pass 1: Duplicated varname uniquified function uniquifyVar(parsed, env) diff --git a/src/parser.jl b/src/parser.jl index 3166113..344c374 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -87,11 +87,25 @@ end patternList = [ + ("if", "if"), + ("then", "then"), + ("else", "else"), + ("True", "bool"), + ("False", "bool"), ("cmt", "[#][^\\r\\n]+"), ("int", "\\d+"), ("id", "[_a-zA-Z][_0-9a-zA-Z]*"), ("lParen", "[\\(]"), ("rParen", "[\\)]"), + ("eq", "[=][=]"), + ("ne", "[!][=]"), + ("lt", "[<]"), + ("le", "[<][=]"), + ("gt", "[>]"), + ("ge", "[>][=]"), + ("and", "[&]"), + ("or", "[\\|]"), + ("not", "[!]"), ("plus", "[+]"), ("funType", "[-][\\>]"), ("minus", "[\\-]"), @@ -182,18 +196,21 @@ end #println(prettyString(test2)) """ -atom = int | id +atom = int | id | bool func = "(" fn_args ")" "=>" body unit = func | "(" exp ")" | atom args = unit ("," unit)* factor = unit "(" args ")" -term = (factor (*|/) term) | factor -exp = (term [(+|-) exp]) | term - +unary = (-|!) factor | factor +term = (unary (*|/) term) | unary +eqcheckee = (term [(+|-) exp]) | term +logical = eqcheckee ("=="|"<"|"<="|">"|">=") eqcheckee | eqcheckee +subexp = logical ("&&"|"||") logical | logical +exp = if subexp then subexp else subexp | subexp letexp = ty id "=" exp ";" body body = exp | letexp """ -atom = typ("int") | typ("id") +atom = typ("int") | typ("id") | typ("bool") @@ -304,8 +321,35 @@ end factor = Psr(factorAux) +function longUnaryAux(input) + rawFunc = seq([(typ("minus") | typ("not")), factor]) + rawRes = rawFunc.fun(input) + if rawRes != nothing + rator = rawRes.matched[1] + rand = rawRes.matched[2] + if rator[2] == "minus" # -a -> 0 - a + matched = [("%prime", "id"), rator, [("0", "int"), rand]] + else + matched = [rator, rand] + end + res = ParserResult(matched, rawRes.remained) + return res + else + return nothing + end +end + +function unaryAux(input) + longUnary = Psr(longUnaryAux) + rawFunc = longUnary | factor + res = rawFunc.fun(input) + return res +end + +unary = Psr(unaryAux) + function longTermAux(input) - rawFunc = seq([factor, (typ("mul") | typ("div")), term]) + rawFunc = seq([unary, (typ("mul") | typ("div")), term]) rawRes = rawFunc.fun(input) if rawRes != nothing #correct the tree a /(b / c) -> (a / b) / c @@ -334,7 +378,7 @@ end function termAux(input) longTerm = Psr(longTermAux) - rawFunc = longTerm | factor + rawFunc = longTerm | unary res = rawFunc.fun(input) return res end @@ -342,8 +386,8 @@ end term = Psr(termAux) -function longExpAux(input) - rawFunc = seq([term, (typ("plus") | typ("minus")), exp]) +function longEqCheckeeAux(input) + rawFunc = seq([term, (typ("plus") | typ("minus")), eqCheckee]) rawRes = rawFunc.fun(input) if rawRes != nothing #correct the tree a -(b - c) -> (a - b) - c @@ -370,15 +414,88 @@ function longExpAux(input) end end +function eqCheckeeAux(input) + longEqCheckee = Psr(longEqCheckeeAux) + rawFunc = longEqCheckee | term + res = rawFunc.fun(input) + return res +end +eqCheckee = Psr(eqCheckeeAux) + + +function longlogicalAux(input) + rawFunc = seq([eqCheckee, (typ("eq") | typ("ne")|typ("lt") | typ("gt")|typ("le") | typ("ge")), eqCheckee]) + rawRes = rawFunc.fun(input) + if rawRes != nothing + rator = rawRes.matched[2] + l = rawRes.matched[1] + r = rawRes.matched[3] + matched = [rator, l, r] + res = ParserResult(matched, rawRes.remained) + return res + else + return nothing + end +end + +function logicalAux(input) + longLogical = Psr(longlogicalAux) + rawFunc = longLogical | eqCheckee + res = rawFunc.fun(input) + return res +end +logical = Psr(logicalAux) + +function longSubExpAux(input) + rawFunc = seq([logical, (typ("and") | typ("or")), logical]) + rawRes = rawFunc.fun(input) + if rawRes != nothing + rator = rawRes.matched[2] + l = rawRes.matched[1] + r = rawRes.matched[3] + matched = [rator, l, r] + res = ParserResult(matched, rawRes.remained) + return res + else + return nothing + end +end + +function subExpAux(input) + longSubExp = Psr(longSubExpAux) + rawFunc = longSubExp | logical + res = rawFunc.fun(input) + return res +end +subExp = Psr(subExpAux) + +function longExpAux(input) + rawFunc = seq([typ("if"), subExp, + typ("then"), subExp, + typ("else"), subExp]) + rawRes = rawFunc.fun(input) + if rawRes != nothing + cond = rawRes.matched[2] + branch1 = rawRes.matched[4] + branch2 = rawRes.matched[6] + matched = [("%if", "id"), cond, branch1, branch2] + res = ParserResult(matched, rawRes.remained) + return res + else + return nothing + end +end + function expAux(input) longExp = Psr(longExpAux) - rawFunc = longExp | term + rawFunc = longExp | subExp res = rawFunc.fun(input) return res end exp = Psr(expAux) + """tyOfArgs = "(" ty ("," ty)* ")" tyHead = tyOfArgs | tyOfFn | id tyOfFn = "(" tyHead -> ty ")" diff --git a/test/prog3.tc b/test/prog3.tc new file mode 100644 index 0000000..4587974 --- /dev/null +++ b/test/prog3.tc @@ -0,0 +1,8 @@ +int a = -10; +int a = False; +int b = (12 + (-3)); +int c = (14 + b); +int d = 20; +if ((True & a) | False) +then 12 +else (if !True then 0 else (d - 3)) \ No newline at end of file