add parser rules and add basic typechecker (not tested)

This commit is contained in:
Tan, Kian-ting 2025-09-07 22:48:50 +08:00
parent 1747789314
commit 1d851522ba
3 changed files with 212 additions and 10 deletions

View file

@ -21,6 +21,83 @@ println("PARSED\n", Parser.prettyStringLisp(parsed))
tmp_var_no = 0 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 # Pass 1: Duplicated varname uniquified
function uniquifyVar(parsed, env) function uniquifyVar(parsed, env)

View file

@ -87,11 +87,25 @@ end
patternList = [ patternList = [
("if", "if"),
("then", "then"),
("else", "else"),
("True", "bool"),
("False", "bool"),
("cmt", "[#][^\\r\\n]+"), ("cmt", "[#][^\\r\\n]+"),
("int", "\\d+"), ("int", "\\d+"),
("id", "[_a-zA-Z][_0-9a-zA-Z]*"), ("id", "[_a-zA-Z][_0-9a-zA-Z]*"),
("lParen", "[\\(]"), ("lParen", "[\\(]"),
("rParen", "[\\)]"), ("rParen", "[\\)]"),
("eq", "[=][=]"),
("ne", "[!][=]"),
("lt", "[<]"),
("le", "[<][=]"),
("gt", "[>]"),
("ge", "[>][=]"),
("and", "[&]"),
("or", "[\\|]"),
("not", "[!]"),
("plus", "[+]"), ("plus", "[+]"),
("funType", "[-][\\>]"), ("funType", "[-][\\>]"),
("minus", "[\\-]"), ("minus", "[\\-]"),
@ -182,18 +196,21 @@ end
#println(prettyString(test2)) #println(prettyString(test2))
""" """
atom = int | id atom = int | id | bool
func = "(" fn_args ")" "=>" body func = "(" fn_args ")" "=>" body
unit = func | "(" exp ")" | atom unit = func | "(" exp ")" | atom
args = unit ("," unit)* args = unit ("," unit)*
factor = unit "(" args ")" factor = unit "(" args ")"
term = (factor (*|/) term) | factor unary = (-|!) factor | factor
exp = (term [(+|-) exp]) | term 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 letexp = ty id "=" exp ";" body
body = exp | letexp body = exp | letexp
""" """
atom = typ("int") | typ("id") atom = typ("int") | typ("id") | typ("bool")
@ -304,8 +321,35 @@ end
factor = Psr(factorAux) 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) function longTermAux(input)
rawFunc = seq([factor, (typ("mul") | typ("div")), term]) rawFunc = seq([unary, (typ("mul") | typ("div")), term])
rawRes = rawFunc.fun(input) rawRes = rawFunc.fun(input)
if rawRes != nothing if rawRes != nothing
#correct the tree a /(b / c) -> (a / b) / c #correct the tree a /(b / c) -> (a / b) / c
@ -334,7 +378,7 @@ end
function termAux(input) function termAux(input)
longTerm = Psr(longTermAux) longTerm = Psr(longTermAux)
rawFunc = longTerm | factor rawFunc = longTerm | unary
res = rawFunc.fun(input) res = rawFunc.fun(input)
return res return res
end end
@ -342,8 +386,8 @@ end
term = Psr(termAux) term = Psr(termAux)
function longExpAux(input) function longEqCheckeeAux(input)
rawFunc = seq([term, (typ("plus") | typ("minus")), exp]) rawFunc = seq([term, (typ("plus") | typ("minus")), eqCheckee])
rawRes = rawFunc.fun(input) rawRes = rawFunc.fun(input)
if rawRes != nothing if rawRes != nothing
#correct the tree a -(b - c) -> (a - b) - c #correct the tree a -(b - c) -> (a - b) - c
@ -370,15 +414,88 @@ function longExpAux(input)
end end
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) function expAux(input)
longExp = Psr(longExpAux) longExp = Psr(longExpAux)
rawFunc = longExp | term rawFunc = longExp | subExp
res = rawFunc.fun(input) res = rawFunc.fun(input)
return res return res
end end
exp = Psr(expAux) exp = Psr(expAux)
"""tyOfArgs = "(" ty ("," ty)* ")" """tyOfArgs = "(" ty ("," ty)* ")"
tyHead = tyOfArgs | tyOfFn | id tyHead = tyOfArgs | tyOfFn | id
tyOfFn = "(" tyHead -> ty ")" tyOfFn = "(" tyHead -> ty ")"

8
test/prog3.tc Normal file
View file

@ -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))