remove excessive warnings in preprocess

This commit is contained in:
Irmen de Jong 2017-12-21 15:12:24 +01:00
parent a228bcd8fc
commit cfb21d7f4f
3 changed files with 36 additions and 30 deletions

View File

@ -6,6 +6,7 @@ Written by Irmen de Jong (irmen@razorvine.net)
License: GNU GPL 3.0, see LICENSE License: GNU GPL 3.0, see LICENSE
""" """
import math
import re import re
import os import os
import shutil import shutil
@ -14,8 +15,8 @@ from typing import Set, List, Tuple, Optional, Any, Dict, Union
from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\ from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
parse_expr_as_string parse_expr_as_string
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \ from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
zeropage, check_value_in_range, coerce_value, char_to_bytevalue, \ zeropage, check_value_in_range, char_to_bytevalue, \
VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \ PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
REGISTER_SYMBOLS, REGISTER_WORDS, REGISTER_BYTES, RESERVED_NAMES REGISTER_SYMBOLS, REGISTER_WORDS, REGISTER_BYTES, RESERVED_NAMES
@ -343,7 +344,8 @@ class ParseResult:
else: else:
cur_block = parser.cur_block cur_block = parser.cur_block
stringvar_name = "il65_str_{:d}".format(id(self)) stringvar_name = "il65_str_{:d}".format(id(self))
cur_block.symbols.define_variable(stringvar_name, cur_block.sourceref, DataType.STRING, value=self.right.value) value = self.right.value
cur_block.symbols.define_variable(stringvar_name, cur_block.sourceref, DataType.STRING, value=value)
self.right.name = stringvar_name self.right.name = stringvar_name
self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name) self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name)
@ -467,6 +469,7 @@ class Parser:
self.cur_block = None # type: ParseResult.Block self.cur_block = None # type: ParseResult.Block
self.root_scope = SymbolTable("<root>", None, None) self.root_scope = SymbolTable("<root>", None, None)
self.ppsymbols = ppsymbols # symboltable from preprocess phase # @todo use this self.ppsymbols = ppsymbols # symboltable from preprocess phase # @todo use this
self.print_block_parsing = True
def load_source(self, filename: str) -> List[Tuple[int, str]]: def load_source(self, filename: str) -> List[Tuple[int, str]]:
with open(filename, "rU") as source: with open(filename, "rU") as source:
@ -506,6 +509,9 @@ class Parser:
self._parse_2() self._parse_2()
return self.result return self.result
def print_warning(self, text: str) -> None:
print(text)
def _parse_1(self) -> None: def _parse_1(self) -> None:
self.parse_header() self.parse_header()
zeropage.configure(self.result.clobberzp) zeropage.configure(self.result.clobberzp)
@ -531,7 +537,7 @@ class Parser:
self.sourceref.column = 0 self.sourceref.column = 0
raise self.PError("The 'main' block should contain the program entry point 'start'") raise self.PError("The 'main' block should contain the program entry point 'start'")
if not any(s for s in block.statements if isinstance(s, ParseResult.ReturnStmt)): if not any(s for s in block.statements if isinstance(s, ParseResult.ReturnStmt)):
print("warning: {}: The 'main' block is lacking a return statement.".format(block.sourceref)) self.print_warning("warning: {}: The 'main' block is lacking a return statement.".format(block.sourceref))
break break
else: else:
raise self.PError("A block named 'main' should be defined for the program's entry point 'start'") raise self.PError("A block named 'main' should be defined for the program's entry point 'start'")
@ -748,10 +754,11 @@ class Parser:
raise self.PError("expected '{' after block") raise self.PError("expected '{' after block")
else: else:
self.next_line() self.next_line()
if self.cur_block.address: if self.print_block_parsing:
print(" parsing block '{:s}' at ${:04x}".format(self.cur_block.name, self.cur_block.address)) if self.cur_block.address:
else: print(" parsing block '{:s}' at ${:04x}".format(self.cur_block.name, self.cur_block.address))
print(" parsing block '{:s}'".format(self.cur_block.name)) else:
print(" parsing block '{:s}'".format(self.cur_block.name))
while True: while True:
line = self.next_line() line = self.next_line()
unstripped_line = line unstripped_line = line
@ -760,7 +767,7 @@ class Parser:
if is_zp_block and any(b.name == "ZP" for b in self.result.blocks): if is_zp_block and any(b.name == "ZP" for b in self.result.blocks):
return None # we already have the ZP block return None # we already have the ZP block
if not self.cur_block.name and not self.cur_block.address: if not self.cur_block.name and not self.cur_block.address:
print("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref)) self.print_warning("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref))
return None return None
return self.cur_block return self.cur_block
if line.startswith("var"): if line.startswith("var"):
@ -827,7 +834,7 @@ class Parser:
if dimensions: if dimensions:
raise self.PError("cannot declare a constant matrix") raise self.PError("cannot declare a constant matrix")
value = parse_expr_as_primitive(valuetext, self.cur_block.symbols, self.sourceref) value = parse_expr_as_primitive(valuetext, self.cur_block.symbols, self.sourceref)
_, value = coerce_value(self.sourceref, datatype, value) _, value = self.coerce_value(self.sourceref, datatype, value)
try: try:
self.cur_block.symbols.define_constant(varname, self.sourceref, datatype, length=length, value=value) self.cur_block.symbols.define_constant(varname, self.sourceref, datatype, length=length, value=value)
except (ValueError, SymbolError) as x: except (ValueError, SymbolError) as x:
@ -889,7 +896,7 @@ class Parser:
def parse_var_def(self, line: str) -> None: def parse_var_def(self, line: str) -> None:
varname, datatype, length, dimensions, valuetext = self.parse_def_common(line, "var", False) varname, datatype, length, dimensions, valuetext = self.parse_def_common(line, "var", False)
value = parse_expr_as_primitive(valuetext, self.cur_block.symbols, self.sourceref) value = parse_expr_as_primitive(valuetext, self.cur_block.symbols, self.sourceref)
_, value = coerce_value(self.sourceref, datatype, value) _, value = self.coerce_value(self.sourceref, datatype, value)
try: try:
self.cur_block.symbols.define_variable(varname, self.sourceref, datatype, self.cur_block.symbols.define_variable(varname, self.sourceref, datatype,
length=length, value=value, matrixsize=dimensions) length=length, value=value, matrixsize=dimensions)
@ -1040,7 +1047,7 @@ class Parser:
raise self.PError("cannot assign {0} to {1}; {2}".format(r_value, lv, reason)) raise self.PError("cannot assign {0} to {1}; {2}".format(r_value, lv, reason))
if lv.datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX): if lv.datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX):
if isinstance(r_value, ParseResult.FloatValue): if isinstance(r_value, ParseResult.FloatValue):
truncated, value = coerce_value(self.sourceref, lv.datatype, r_value.value) truncated, value = self.coerce_value(self.sourceref, lv.datatype, r_value.value)
if truncated: if truncated:
r_value = ParseResult.IntegerValue(int(value), datatype=lv.datatype, name=r_value.name) r_value = ParseResult.IntegerValue(int(value), datatype=lv.datatype, name=r_value.name)
return ParseResult.AssignmentStmt(l_values, r_value, self.sourceref.line) return ParseResult.AssignmentStmt(l_values, r_value, self.sourceref.line)
@ -1249,6 +1256,19 @@ class Parser:
return (parse_expr_as_int(xs, self.cur_block.symbols, self.sourceref), return (parse_expr_as_int(xs, self.cur_block.symbols, self.sourceref),
parse_expr_as_int(ys, self.cur_block.symbols, self.sourceref)) parse_expr_as_int(ys, self.cur_block.symbols, self.sourceref))
def coerce_value(self, sourceref: SourceRef, datatype: DataType, value: PrimitiveType) -> Tuple[bool, PrimitiveType]:
# if we're a BYTE type, and the value is a single character, convert it to the numeric value
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
if len(value) == 1:
return True, char_to_bytevalue(value)
# if we're an integer value and the passed value is float, truncate it (and give a warning)
if datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX) and type(value) is float:
frac = math.modf(value) # type:ignore
if frac != 0:
self.print_warning("warning: {}: Float value truncated.".format(sourceref))
return True, int(value)
return False, value
def psplit(self, sentence: str, separators: str=" \t", lparen: str="(", rparen: str=")") -> List[str]: def psplit(self, sentence: str, separators: str=" \t", lparen: str="(", rparen: str=")") -> List[str]:
"""split a sentence but not on separators within parenthesis""" """split a sentence but not on separators within parenthesis"""
nb_brackets = 0 nb_brackets = 0

View File

@ -13,6 +13,7 @@ from .parse import Parser, ParseResult, SymbolTable, SymbolDefinition
class PreprocessingParser(Parser): class PreprocessingParser(Parser):
def __init__(self, filename: str) -> None: def __init__(self, filename: str) -> None:
super().__init__(filename, "", parsing_import=True) super().__init__(filename, "", parsing_import=True)
self.print_block_parsing = False
def preprocess(self) -> Tuple[List[Tuple[int, str]], SymbolTable]: def preprocess(self) -> Tuple[List[Tuple[int, str]], SymbolTable]:
def cleanup_table(symbols: SymbolTable): def cleanup_table(symbols: SymbolTable):
@ -26,6 +27,9 @@ class PreprocessingParser(Parser):
cleanup_table(self.root_scope) cleanup_table(self.root_scope)
return self.lines, self.root_scope return self.lines, self.root_scope
def print_warning(self, text: str) -> None:
pass
def load_source(self, filename: str) -> List[Tuple[int, str]]: def load_source(self, filename: str) -> List[Tuple[int, str]]:
lines = super().load_source(filename) lines = super().load_source(filename)
# can do some additional source-level preprocessing here # can do some additional source-level preprocessing here

View File

@ -329,8 +329,6 @@ class SymbolTable:
range_error = check_value_in_range(datatype, register, length, value) range_error = check_value_in_range(datatype, register, length, value)
if range_error: if range_error:
raise ValueError(range_error) raise ValueError(range_error)
if type(value) in (int, float):
_, value = coerce_value(sourceref, datatype, value) # type: ignore
allocate = address is None allocate = address is None
if datatype == DataType.BYTE: if datatype == DataType.BYTE:
if allocate and self.name == "ZP": if allocate and self.name == "ZP":
@ -389,8 +387,6 @@ class SymbolTable:
# this defines a new constant and also checks if the value is allowed for the data type. # this defines a new constant and also checks if the value is allowed for the data type.
assert value is not None assert value is not None
self.check_identifier_valid(name, sourceref) self.check_identifier_valid(name, sourceref)
if type(value) in (int, float):
_, value = coerce_value(sourceref, datatype, value) # type: ignore
range_error = check_value_in_range(datatype, "", length, value) range_error = check_value_in_range(datatype, "", length, value)
if range_error: if range_error:
raise ValueError(range_error) raise ValueError(range_error)
@ -477,20 +473,6 @@ class Eval_symbol_dict(dict):
raise SymbolError("no support for non-constant expression evaluation yet") raise SymbolError("no support for non-constant expression evaluation yet")
def coerce_value(sourceref: SourceRef, datatype: DataType, value: PrimitiveType) -> Tuple[bool, PrimitiveType]:
# if we're a BYTE type, and the value is a single character, convert it to the numeric value
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
if len(value) == 1:
return True, char_to_bytevalue(value)
# if we're an integer value and the passed value is float, truncate it (and give a warning)
if datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX) and type(value) is float:
frac = math.modf(value) # type:ignore
if frac != 0:
print("warning: {}: Float value truncated.".format(sourceref))
return True, int(value)
return False, value
def check_value_in_range(datatype: DataType, register: str, length: int, value: PrimitiveType) -> Optional[str]: def check_value_in_range(datatype: DataType, register: str, length: int, value: PrimitiveType) -> Optional[str]:
if register: if register:
if register in REGISTER_BYTES: if register in REGISTER_BYTES: