Skip to content

Commit

Permalink
Merge branch 'master' into comments
Browse files Browse the repository at this point in the history
  • Loading branch information
frankhart2018 authored Jul 16, 2020
2 parents cf42660 + 41de7e3 commit 6b6c8f2
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 17 deletions.
46 changes: 46 additions & 0 deletions compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ def compile(opcodes, c_filename, table):
code += '", ' + str(val[0]) + ");\n"
else:
code += '", &' + str(val[0]) + ");\n"
#If opcode is of type ptr_assign then generate a declarative statement
elif opcode.type == "ptr_assign":
code = ""

# val contains - <identifier>---<expression>---<count_ast>, split that into a list
val = opcode.val.split("---")

# Get the datatye of the variable
_, dtype, _ = table.get_by_id(table.get_by_symbol(val[0]))

# Helper Dictionaries
get_data_type = {"i": "int", "s": "char*", "f": "float", "d": "double"}
get_placeholder = {"i": "d", "s": "s", "f": "f", "d": "lf"}

# If it is of string type then change it to char <identifier>[]
if dtype == "string":
dtype = "char*"
code += "\t" + dtype +" " + "*"*int(val[2]) + str(val[0]) + " = " + str(val[1]) + ";\n"


# If opcode is of type var_no_assign then generate a declaration statement
elif opcode.type == "var_no_assign":
Expand All @@ -128,6 +147,18 @@ def compile(opcodes, c_filename, table):
opcode.dtype = str(dtype) if dtype is not None else "not_known"

code += "\t" + opcode.dtype + " " + str(opcode.val) + ";\n"

#If opcode is of type ptr_no_assign then generate declaration statement
elif opcode.type == "ptr_no_assign":
val = opcode.val.split("---")
# Get the datatye of the variable
_, dtype, _ = table.get_by_id(table.get_by_symbol(val[0]))
# Check if dtype could be inferred or not
opcode.dtype = str(dtype) if dtype is not None else "not_known"
if opcode.dtype == 'string':
opcode.dtype = 'char'
code += "\t" + opcode.dtype + " *" + str(opcode.val) + ";\n"

# If opcode is of type assign then generate an assignment statement
elif opcode.type == "assign":
# val contains - <identifier>---<expression>, split that into a list
Expand All @@ -147,6 +178,12 @@ def compile(opcodes, c_filename, table):
code += "\t" + 'printf("' + str(val[1]) + '");\n'
code += "\t" + 'scanf("%' + placeholder + '", &' + str(val[0]) + ");\n"

# If opcode is of type ptr_only_assign then generate an assignment statement
elif opcode.type == "ptr_only_assign":
# val contains - <identifier>---<expression>---<count_ast>, split that into a list
val = opcode.val.split("---")
code += "\t" + int(val[2])*'*'+val[0] + " = " + val[1] + ";\n"

# If opcode is of type unary then generate an uanry statement
elif opcode.type == "unary":
# val contains - <identifier>---<expression>, split that into a list
Expand Down Expand Up @@ -275,6 +312,15 @@ def compile(opcodes, c_filename, table):
# If opcode is of type multi_line_comment the generate single comment line
elif opcode.type == "multi_line_comment":
code += "/* %s*/\n" %opcode.val
# If opcode is of type switch then generate switch statement
elif opcode.type == "switch":
code += "\tswitch(" + opcode.val + ") {\n";
# If opcode is of type case then generate case statement
elif opcode.type == "case":
code += "\tcase " + opcode.val + ":\n"
# If opcode is of type default then generate default statement
elif opcode.type == "default":
code += "\tdefault:\n"

outside_code, ccode = compile_func_main_code(
outside_code, ccode, outside_main, code
Expand Down
53 changes: 50 additions & 3 deletions lexical_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def is_keyword(value):
"continue",
"input",
"exit",
"switch",
"case",
"default"
]


Expand Down Expand Up @@ -70,7 +73,7 @@ def numeric_val(source_code, i, table, line_num):
if numeric_constant.count(".") > 1:
error(
"Invalid numeric constant, cannot have more than one decimal point in a"
" number!"
" number!" , line_num
)

# Check the length after . to distinguish between float and double
Expand Down Expand Up @@ -116,7 +119,7 @@ def string_val(source_code, i, table, line_num):
# Loop until we get a non-digit character
while source_code[i] != '"':
if source_code[i] == "\0":
error("Unterminated string!")
error("Unterminated string!", line_num)

string_constant += source_code[i]
i += 1
Expand Down Expand Up @@ -171,6 +174,40 @@ def keyword_identifier(source_code, i, table, line_num):
# Check if identifier is in symbol table
id = table.get_by_symbol(value)

C_keywords = ['break',
'else',
'long',
'switch',
'case',
'enum',
'register',
'typedef',
'char',
'extern',
'return',
'union',
'const',
'float',
'short',
'unsigned',
'continue',
'for',
'signed',
'void',
'default',
'goto',
'sizeof',
'volatile',
'do',
'if',
'static',
'while'
]

#Check if identifier is a keyword in class
if value in C_keywords:
error("A keyword cannot be an identifier - %s" % value, line_num)

# If identifier is not in symbol table then give a placeholder datatype var
if id == -1:
id = table.entry(value, "var", "variable")
Expand All @@ -196,7 +233,7 @@ def lexical_analyze(filename, table):

# Check if file extension is .simc or not
if "." not in filename or filename.split(".")[-1] != "simc":
error("Incorrect file extension")
error("Incorrect file extension", line_num)

# Read the entire source code as a string
source_code = open(filename, "r").read()
Expand Down Expand Up @@ -310,6 +347,11 @@ def lexical_analyze(filename, table):
tokens.append(Token("multiply", "", line_num))
i += 1

#Identifying 'address of' token
elif source_code[i] == '&':
tokens.append(Token("address_of", "", line_num))
i += 1

# Identifying divide_equal or divide token
elif source_code[i] == "/":
if source_code[i + 1] == "=":
Expand Down Expand Up @@ -372,6 +414,11 @@ def lexical_analyze(filename, table):
tokens.append(Token("less_than_equal", "", line_num))
i += 2

# Identifiying colon token
elif source_code[i] == ':':
tokens.append(Token("colon", "", line_num))
i += 1

# Otherwise increment the index
else:
i += 1
Expand Down
3 changes: 3 additions & 0 deletions op_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@ def opcode2dig(self, str_type):
"while": 6,
"if": 7,
"exit": 8,
"ptr_no_assign": 9,
"ptr_assign" :10,
"ptr_only_assign" :11,
}
return dic.get(str_type, 0)
3 changes: 3 additions & 0 deletions simc.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,8 @@
# Get opcodes from parser
op_codes = parse(tokens, table)

for op_code in op_codes:
print(op_code)

# Compile to C code
compile(op_codes, c_filename, table)
117 changes: 104 additions & 13 deletions simc_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ def expression(
"exit",
"right_paren",
"newline",
"call_end"
"call_end",
"address_of"
]:
# Check for function call
if tokens[i].type == 'id' and tokens[i+1].type == 'left_paren':
Expand Down Expand Up @@ -270,7 +271,8 @@ def expression(
"or": " || ",
"comma": ",",
"left_paren": "(",
"right_paren": ")"
"right_paren": ")",
"address_of": "&"
}

if(expect_paren and tokens[i].type == 'right_paren' and tokens[i+1].type in ['newline', 'left_brace']):
Expand Down Expand Up @@ -374,6 +376,23 @@ def print_statement(tokens, i, table, func_ret_type):
return OpCode("print", op_value), i + 1, func_ret_type


def check_ptr(tokens,i):
#Check if a pointer is being declared
is_ptr = False
#Count the depth of pointer
count_ast = 0
if(tokens[i].type == "multiply"):
j = 0
while(tokens[i+j].type == "multiply"):
j += 1
i += j
count_ast = j
is_ptr = True
return is_ptr, count_ast, i
else:
return False, 0, i


def var_statement(tokens, i, table, func_ret_type):
"""
Parse variable declaration [/initialization] statement
Expand All @@ -400,9 +419,11 @@ def var_statement(tokens, i, table, func_ret_type):
operator -> + | - | * | /
"""

is_ptr, count_ast, i = check_ptr(tokens,i)
# Check if identifier is present after var
check_if(tokens[i].type, "id", "Expected id after var keyword", tokens[i].line_num)


# Check if variable is also initialized
if i + 1 < len(tokens) and tokens[i + 1].type == "assignment":
# Store the index of identifier
Expand All @@ -426,16 +447,27 @@ def var_statement(tokens, i, table, func_ret_type):
# Modify datatype of the identifier
table.symbol_table[tokens[id_idx].val][1] = prec_to_type[op_type]

# Return the opcode and i (the token after var statement)
return (
OpCode(
"var_assign",
table.symbol_table[tokens[id_idx].val][0] + "---" + op_value,
prec_to_type[op_type],
),
i,
func_ret_type
)
if(is_ptr):
return (
OpCode(
"ptr_assign",
table.symbol_table[tokens[id_idx].val][0] + "---" + op_value+ "---" +str(count_ast),
prec_to_type[op_type],
),
i,
func_ret_type
)
else:
# Return the opcode and i (the token after var statement)
return (
OpCode(
"var_assign",
table.symbol_table[tokens[id_idx].val][0] + "---" + op_value,
prec_to_type[op_type],
),
i,
func_ret_type
)
else:
# Get the value from symbol table by id
value, type, _ = table.get_by_id(tokens[i].val)
Expand All @@ -457,6 +489,9 @@ def var_statement(tokens, i, table, func_ret_type):
table.symbol_table[tokens[i].val][1] = "declared"

# Return the opcode and i+1 (the token after var statement)
if is_ptr:
return OpCode("ptr_no_assign", value), i + 1, func_ret_type

return OpCode("var_no_assign", value), i + 1, func_ret_type


Expand Down Expand Up @@ -485,6 +520,17 @@ def assign_statement(tokens, i, table, func_ret_type):
operator -> + | - | * | /
"""

#Check if the identifier is a pointer
is_ptr = False
#count depth of pointer
count_ast = 0
if(tokens[i-2].type == "multiply"):
j = -2
while(tokens[j+i].type == "multiply"):
j -= 1
count_ast = -1*j-2
is_ptr = True

# Check if variable is declared or not
value, type, _ = table.get_by_id(tokens[i - 1].val)

Expand Down Expand Up @@ -526,7 +572,15 @@ def assign_statement(tokens, i, table, func_ret_type):

# Modify datatype of the identifier
table.symbol_table[tokens[id_idx].val][1] = prec_to_type[op_type]

#Check if a pointer is being assigned
if(is_ptr):
return (
OpCode(
"ptr_only_assign", table.symbol_table[tokens[id_idx].val][0] + "---" + op_value +"---"+str(count_ast), ""
),
i,
func_ret_type
)
# Return the opcode and i (the token after assign statement)
return (
OpCode(
Expand Down Expand Up @@ -963,6 +1017,30 @@ def exit_statement(tokens, i, table, func_ret_type):

return OpCode("exit", op_value[:-1]), i, func_ret_type

def switch_statement(tokens, i, table, func_ret_type):

check_if(tokens[i].type, "left_paren", "Expected ( after switch", tokens[i].line_num)

op_value, _, i, func_ret_type = expression(
tokens, i+1, table, "Expected expression inside switch statement", func_ret_type=func_ret_type
)

check_if(tokens[i-1].type, "right_paren", "Expected ) after expression in switch", tokens[i-1].line_num)

check_if(tokens[i+1].type, "left_brace", "Expected { after switch statement", tokens[i].line_num)

return OpCode("switch", op_value[:-1], ""), i+1, func_ret_type

def case_statement(tokens, i, table, func_ret_type):

op_value, _, i, func_ret_type = expression(
tokens, i, table, "Expected expected expression after case", expect_paren=False, func_ret_type=func_ret_type
)

check_if(tokens[i].type, "colon", "Expected : after case in switch statement", tokens[i].line_num)

return OpCode("case", op_value, ""), i+1, func_ret_type

def parse(tokens, table):
"""
Parse tokens and generate opcodes
Expand Down Expand Up @@ -1134,6 +1212,19 @@ def parse(tokens, table):
elif tokens[i].type == "multi_line_comment":
op_codes.append(OpCode("multi_line_comment", tokens[i].val, ""))
i += 1
# If token is of type switch then generate switch opcode
elif tokens[i].type == "switch":
switch_opcode, i, func_ret_type = switch_statement(tokens, i+1, table, func_ret_type)
op_codes.append(switch_opcode)
# If token is of type case then generate case opcode
elif tokens[i].type == "case":
case_opcode, i, func_ret_type = case_statement(tokens, i+1, table, func_ret_type)
op_codes.append(case_opcode)
# If token is of type default then generate default opcode
elif tokens[i].type == "default":
check_if(tokens[i+1].type, "colon", "Expected : after default statement in switch", tokens[i+1].line_num)
op_codes.append(OpCode("default", "", ""))
i += 2
# Otherwise increment the index
else:
i += 1
Expand Down
2 changes: 1 addition & 1 deletion test.simc
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ MAIN

// testing single line comment


END_MAIN

0 comments on commit 6b6c8f2

Please sign in to comment.