as: parse: use tokens' location tags for diagnostics

This commit is contained in:
Mikael Pettersson 2019-12-26 21:10:03 +01:00
parent 34202b2e2f
commit 589e2c40bb

View File

@ -43,11 +43,11 @@ stmt(ScanState) ->
{ok, {_Location, ?T_DOT_SUBSECTION}} -> dot_subsection(ScanState);
{ok, {_Location, ?T_DOT_TEXT}} -> dot_text(ScanState);
{ok, {_Location, ?T_DOT_TYPE}} -> dot_type(ScanState);
{ok, {_Location, {?T_SYMBOL, Name}}} -> stmt_after_symbol(ScanState, Name);
{ok, {Location, {?T_SYMBOL, Name}}} -> stmt_after_symbol(ScanState, Location, Name);
{ok, {_Location, {?T_UINTEGER, UInt}}} -> stmt_after_uinteger(ScanState, UInt);
{ok, {_Location, ?T_NEWLINE}} -> stmt(ScanState);
{ok, {_Location, ?T_EOF}} -> eof;
ScanRes -> badtok(ScanState, "expected directive, label, or instruction", ScanRes)
ScanRes -> badtok("expected directive, label, or instruction", ScanRes)
end.
%% Instructions and labels -----------------------------------------------------
@ -94,120 +94,120 @@ stmt(ScanState) ->
%% This means that "opcode (...)" is interpreted as having a displacement but no
%% index. Use "opcode 0(index)" if an index with zero displacement is needed.
stmt_after_symbol(ScanState, Name) ->
stmt_after_symbol(ScanState, Location, Name) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_COLON}} -> {ok, #s_label{name = Name}};
{ok, {_Location, ?T_NEWLINE}} -> make_insn(ScanState, Name, false, false, false, false);
{ok, {_Location, {?T_UINTEGER, UInt}}} -> insn_uint(ScanState, Name, UInt);
{ok, {_Location, {?T_SYMBOL, Symbol}}} -> insn_symbol(ScanState, Name, Symbol);
{ok, {_Location, ?T_NEWLINE}} -> make_insn(Location, Name, false, false, false, false);
{ok, {_Location, {?T_UINTEGER, UInt}}} -> insn_uint(ScanState, Location, Name, UInt);
{ok, {_Location, {?T_SYMBOL, Symbol}}} -> insn_symbol(ScanState, Location, Name, Symbol);
{ok, {_Location, {?T_LOCAL_LABEL, Number, Direction}}} ->
insn_local_label(ScanState, Name, Number, Direction);
ScanRes -> badtok(ScanState, "junk after symbol", ScanRes)
insn_local_label(ScanState, Location, Name, Number, Direction);
ScanRes -> badtok("junk after symbol", ScanRes)
end.
%% <stmt> ::= <uinteger> . ":"
stmt_after_uinteger(ScanState, UInt) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_COLON}} -> {ok, #s_local_label{number = UInt}};
ScanRes -> badtok(ScanState, "junk after symbol", ScanRes)
ScanRes -> badtok("junk after symbol", ScanRes)
end.
%% Seen "<symbol> <uinteger>". The <uinteger> is the <accumulator> if followed
%% by ",", otherwise (the start of) the <displacement>.
insn_uint(ScanState, Name, UInt) ->
insn_uint(ScanState, Location, Name, UInt) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_COMMA}} -> % the Uint is the Accumulator, parse EA next
insn_ea(ScanState, Name, _AccOrDev = UInt);
insn_ea(ScanState, Location, Name, _AccOrDev = UInt);
{ok, {_Location, ?T_LPAREN}} -> % the Uint is the Displacement, parse Index next
Displacement = #e_integer{value = UInt},
insn_ea_index(ScanState, Name, _AccOrDev = false, _At = false, Displacement);
insn_ea_index(ScanState, Location, Name, _AccOrDev = false, _At = false, Displacement);
{ok, {_Location, ?T_NEWLINE}} -> % the Uint is the Displacement
Displacement = #e_integer{value = UInt},
make_insn(ScanState, Name, _AccOrDev = false, _At = false, Displacement, _Index = false);
ScanRes -> badtok(ScanState, "junk after <symbol> <uinteger>", ScanRes)
make_insn(Location, Name, _AccOrDev = false, _At = false, Displacement, _Index = false);
ScanRes -> badtok("junk after <symbol> <uinteger>", ScanRes)
end.
%% Seen "<symbol> <symbol2>". The <symbol2> is (the start of) the <displacement>.
%% TODO: permit <symbol2> to be the <accumulator> (named register or device).
insn_symbol(ScanState, Name, Symbol2) ->
insn_symbol(ScanState, Location, Name, Symbol2) ->
Displacement = #e_symbol{name = Symbol2},
insn_ea_disp(ScanState, Name, _AccOrDev = false, _At = false, Displacement).
insn_ea_disp(ScanState, Location, Name, _AccOrDev = false, _At = false, Displacement).
%% Seen "<symbol> <local label>". The <local label> is (the start of) the <displacement>.
insn_local_label(ScanState, Name, Number, Direction) ->
insn_local_label(ScanState, Location, Name, Number, Direction) ->
Displacement = #e_local_label{number = Number, direction = Direction},
insn_ea_disp(ScanState, Name, _AccOrDev = false, _At = false, Displacement).
insn_ea_disp(ScanState, Location, Name, _AccOrDev = false, _At = false, Displacement).
%% <symbol> <accordev> "," . [ ["@"] <displacement> ["(" <index> ")"] ] <newline>
insn_ea(ScanState, Name, AccOrDev) ->
insn_ea(ScanState, Location, Name, AccOrDev) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_NEWLINE}} ->
make_insn(ScanState, Name, AccOrDev, _At = false, _Displacement = false, _Index = false);
{ok, {_Location, ?T_AT}} -> insn_ea_at(ScanState, Name, AccOrDev);
make_insn(Location, Name, AccOrDev, _At = false, _Displacement = false, _Index = false);
{ok, {_Location, ?T_AT}} -> insn_ea_at(ScanState, Location, Name, AccOrDev);
{ok, {_Lcation, {?T_UINTEGER, UInt}}} ->
Displacement = #e_integer{value = UInt},
insn_ea_disp(ScanState, Name, AccOrDev, _At = false, Displacement);
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = false, Displacement);
{ok, {_Location, {?T_SYMBOL, Symbol}}} ->
Displacement = #e_symbol{name = Symbol},
insn_ea_disp(ScanState, Name, AccOrDev, _At = false, Displacement);
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = false, Displacement);
{ok, {_Location, {?T_LOCAL_LABEL, Number, Direction}}} ->
Displacement = #e_local_label{number = Number, direction = Direction},
insn_ea_disp(ScanState, Name, AccOrDev, _At = false, Displacement);
ScanRes -> badtok(ScanState, "junk after comma", ScanRes)
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = false, Displacement);
ScanRes -> badtok("junk after comma", ScanRes)
end.
%% <symbol> [<accordev> ","] "@" . <displacement> ["(" <index> ")"] <newline>
insn_ea_at(ScanState, Name, AccOrDev) ->
insn_ea_at(ScanState, Location, Name, AccOrDev) ->
case scan:token(ScanState) of
{ok, {_Location, {?T_UINTEGER, UInt}}} ->
Displacement = #e_integer{value = UInt},
insn_ea_disp(ScanState, Name, AccOrDev, _At = true, Displacement);
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = true, Displacement);
{ok, {_Location, {?T_SYMBOL, Symbol}}} ->
Displacement = #e_symbol{name = Symbol},
insn_ea_disp(ScanState, Name, AccOrDev, _At = true, Displacement);
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = true, Displacement);
{ok, {_Location, {?T_LOCAL_LABEL, Number, Direction}}} ->
Displacement = #e_local_label{number = Number, direction = Direction},
insn_ea_disp(ScanState, Name, AccOrDev, _At = true, Displacement);
ScanRes -> badtok(ScanState, "junk after @", ScanRes)
insn_ea_disp(ScanState, Location, Name, AccOrDev, _At = true, Displacement);
ScanRes -> badtok("junk after @", ScanRes)
end.
%% <symbol> [<accordev> ","] ["@"] <displacement> . ["(" <index> ")"] <newline>
insn_ea_disp(ScanState, Name, AccOrDev, At, Displacement) ->
insn_ea_disp(ScanState, Location, Name, AccOrDev, At, Displacement) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_LPAREN}} -> insn_ea_index(ScanState, Name, AccOrDev, At, Displacement);
{ok, {_Location, ?T_NEWLINE}} -> make_insn(ScanState, Name, AccOrDev, At, Displacement, _Index = false);
ScanRes -> badtok(ScanState, "junk after <displacement>", ScanRes)
{ok, {_Location, ?T_LPAREN}} -> insn_ea_index(ScanState, Location, Name, AccOrDev, At, Displacement);
{ok, {_Location, ?T_NEWLINE}} -> make_insn(Location, Name, AccOrDev, At, Displacement, _Index = false);
ScanRes -> badtok("junk after <displacement>", ScanRes)
end.
%% <symbol> [<accordev> ","] ["@"] <displacement> "(" . <index> ")" <newline>
insn_ea_index(ScanState, Name, AccOrDev, At, Displacement) ->
insn_ea_index(ScanState, Location, Name, AccOrDev, At, Displacement) ->
case scan:token(ScanState) of
{ok, {_Location1, {?T_UINTEGER, Index}}} when Index =< 8#17 ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_RPAREN}} ->
case scan:token(ScanState) of
{ok, {_Location3, ?T_NEWLINE}} ->
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index);
ScanRes -> badtok(ScanState, "junk after <index>", ScanRes)
make_insn(Location, Name, AccOrDev, At, Displacement, Index);
ScanRes -> badtok("junk after <index>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
ScanRes -> badtok("junk in <index>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
ScanRes -> badtok("junk in <index>", ScanRes)
end.
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index) ->
make_insn(Location, Name, AccOrDev, At, Displacement, Index) ->
Models = ?PDP10_KL10_271, % FIXME: make dynamic
case pdp10_opcodes:insn_from_name(Models, Name, AccOrDev =/= false) of
false -> badinsn(ScanState, "invalid mnemonic ~s", Name);
false -> badinsn(Location, "invalid mnemonic ~s", Name);
#pdp10_insn_desc{ high13 = High13
, format = Format
, e_unused = EUnused
, extended = false % TODO: handle extended opcodes
} ->
case make_high13(ScanState, Name, AccOrDev, High13, Format) of
case make_high13(Location, Name, AccOrDev, High13, Format) of
{error, _Reason} = Error -> Error;
{ok, FinalHigh13} ->
case check_e(ScanState, Name, At, Displacement, Index, EUnused) of
case check_e(Location, Name, At, Displacement, Index, EUnused) of
{error, _Reason} = Error -> Error;
ok -> {ok, #s_insn{ high13 = FinalHigh13
, at = At
@ -221,14 +221,14 @@ make_insn(ScanState, Name, AccOrDev, At, Displacement, Index) ->
end
end.
make_high13(ScanState, Name, AccOrDev, High13, Format) ->
make_high13(Location, Name, AccOrDev, High13, Format) ->
case {Format, AccOrDev} of
{?PDP10_INSN_A_OPCODE, false} ->
{ok, High13};
{?PDP10_INSN_A_OPCODE, _} ->
badinsn(ScanState, "~s: extraneous accumulator operand", Name);
badinsn(Location, "~s: extraneous accumulator operand", Name);
{?PDP10_INSN_IO, false} ->
badinsn(ScanState, "~s: missing device operand", Name);
badinsn(Location, "~s: missing device operand", Name);
{?PDP10_INSN_IO, _} ->
make_high13_io(High13, AccOrDev);
{?PDP10_INSN_BASIC, false} ->
@ -236,9 +236,9 @@ make_high13(ScanState, Name, AccOrDev, High13, Format) ->
{?PDP10_INSN_BASIC, _} ->
make_high13_basic(High13, AccOrDev);
{?PDP10_INSN_A_NONZERO, false} ->
badinsn(ScanState, "~s: missing accumulator operand", Name);
badinsn(Location, "~s: missing accumulator operand", Name);
{?PDP10_INSN_A_NONZERO, 0} ->
badinsn(ScanState, "~s: accumulator must not be zero", Name);
badinsn(Location, "~s: accumulator must not be zero", Name);
{?PDP10_INSN_A_NONZERO, _} ->
make_high13_basic(High13, AccOrDev)
end.
@ -251,17 +251,17 @@ make_high13_io(High13, Device) ->
Mask = ((1 bsl 7) - 1),
{ok, (High13 band bnot (Mask bsl 3)) bor ((Device band Mask) bsl 3)}.
check_e(ScanState, Name, At, Displacement, Index, EUnused) ->
check_e(Location, Name, At, Displacement, Index, EUnused) ->
HaveE = At orelse Displacement =/= false orelse Index =/= false,
case {EUnused, HaveE} of
{true, false} -> ok;
{true, true} -> badinsn(ScanState, "~s: extraneous address operand", Name);
{false, false} -> badinsn(ScanState, "~s: missing address operand", Name);
{true, true} -> badinsn(Location, "~s: extraneous address operand", Name);
{false, false} -> badinsn(Location, "~s: missing address operand", Name);
{false, true} -> ok
end.
badinsn(ScanState, Fmt, Mnemonic) ->
fmterr(ScanState, Fmt, [Mnemonic]).
badinsn(Location, Fmt, Mnemonic) ->
fmterr(Location, Fmt, [Mnemonic]).
%% Directives ------------------------------------------------------------------
@ -271,9 +271,9 @@ dot_data(ScanState) ->
{ok, {_Location1, {?T_UINTEGER, Nr}}} ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_NEWLINE}} -> {ok, #s_dot_data{nr = Nr}};
ScanRes -> badtok(ScanState, "junk after .data <nr>", ScanRes)
ScanRes -> badtok("junk after .data <nr>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .data", ScanRes)
ScanRes -> badtok("junk after .data", ScanRes)
end.
dot_file(ScanState) ->
@ -289,9 +289,9 @@ dot_file_or_ident(ScanState, MkStmt, ErrMsg) ->
{ok, {_Location1, {?T_STRING, String}}} ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_NEWLINE}} -> {ok, MkStmt(String)};
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
ScanRes -> badtok(ErrMsg, ScanRes)
end;
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
ScanRes -> badtok(ErrMsg, ScanRes)
end.
dot_globl(ScanState) ->
@ -299,21 +299,21 @@ dot_globl(ScanState) ->
{ok, {_Location1, {?T_SYMBOL, Name}}} ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_NEWLINE}} -> {ok, #s_dot_globl{name = Name}};
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
ScanRes -> badtok("junk after .globl", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
ScanRes -> badtok("junk after .globl", ScanRes)
end.
dot_popsection(ScanState) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_NEWLINE}} -> {ok, #s_dot_popsection{}};
ScanRes -> badtok(ScanState, "junk after .popsection", ScanRes)
ScanRes -> badtok("junk after .popsection", ScanRes)
end.
dot_previous(ScanState) ->
case scan:token(ScanState) of
{ok, {_Location, ?T_NEWLINE}} -> {ok, #s_dot_previous{}};
ScanRes -> badtok(ScanState, "junk after .previous", ScanRes)
ScanRes -> badtok("junk after .previous", ScanRes)
end.
%% For now only accepts ".pushsection <name> [, <nr>]". TODO: extend
@ -324,7 +324,7 @@ dot_pushsection(ScanState) ->
%% TODO: do we need a general mapping from reserved to plain symbols?
{ok, {_Location, ?T_DOT_DATA}} -> dot_pushsection(ScanState, _Name = ".data");
{ok, {_Location, ?T_DOT_TEXT}} -> dot_pushsection(ScanState, _Name = ".text");
ScanRes -> badtok(ScanState, "junk after .pushsection", ScanRes)
ScanRes -> badtok("junk after .pushsection", ScanRes)
end.
%% Seen ".pushsection <name>", expects "[, <nr>]".
@ -336,11 +336,11 @@ dot_pushsection(ScanState, Name) ->
{ok, {_Location2, {?T_UINTEGER, Nr}}} ->
case scan:token(ScanState) of
{ok, {_Location3, ?T_NEWLINE}} -> {ok, #s_dot_pushsection{name = Name, nr = Nr}};
ScanRes -> badtok(ScanState, "junk after .pushsection <name>, <nr>", ScanRes)
ScanRes -> badtok("junk after .pushsection <name>, <nr>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .pushsection <name>,", ScanRes)
ScanRes -> badtok("junk after .pushsection <name>,", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .pushsection <name>", ScanRes)
ScanRes -> badtok("junk after .pushsection <name>", ScanRes)
end.
%% For now only accepts ".size <sym>,.-<sym>". TODO: extend
@ -357,17 +357,17 @@ dot_size(ScanState) ->
{ok, {_Location5, {?T_SYMBOL, Name}}} -> % same Name as above
case scan:token(ScanState) of
{ok, {_Location6, ?T_NEWLINE}} -> {ok, #s_dot_size{name = Name}};
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
ScanRes -> badtok("junk after .size", ScanRes)
end.
dot_subsection(ScanState) ->
@ -375,9 +375,9 @@ dot_subsection(ScanState) ->
{ok, {_Location1, {?T_UINTEGER, Nr}}} ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_NEWLINE}} -> {ok, #s_dot_subsection{nr = Nr}};
ScanRes -> badtok(ScanState, "junk after .subsection <nr>", ScanRes)
ScanRes -> badtok("junk after .subsection <nr>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .subsection", ScanRes)
ScanRes -> badtok("junk after .subsection", ScanRes)
end.
dot_text(ScanState) ->
@ -386,9 +386,9 @@ dot_text(ScanState) ->
{ok, {_Location1, {?T_UINTEGER, Nr}}} ->
case scan:token(ScanState) of
{ok, {_Location2, ?T_NEWLINE}} -> {ok, #s_dot_text{nr = Nr}};
ScanRes -> badtok(ScanState, "junk after .text <nr>", ScanRes)
ScanRes -> badtok("junk after .text <nr>", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .text", ScanRes)
ScanRes -> badtok("junk after .text", ScanRes)
end.
%% For now only accepts ".type <sym>,@function". TODO: extend
@ -403,25 +403,24 @@ dot_type(ScanState) ->
{ok, {_Location4, {?T_SYMBOL, "function"}}} ->
case scan:token(ScanState) of
{ok, {_Location5, ?T_NEWLINE}} -> {ok, #s_dot_type{name = Name}};
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
ScanRes -> badtok("junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
ScanRes -> badtok("junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
ScanRes -> badtok("junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
ScanRes -> badtok("junk after .type", ScanRes)
end;
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
ScanRes -> badtok("junk after .type", ScanRes)
end.
%% Error reporting -------------------------------------------------------------
badtok(_ScanState, _ErrMsg, {error, _Reason} = Error) -> Error;
badtok(ScanState, ErrMsg, {ok, {_Location, Token}}) ->
fmterr(ScanState, ErrMsg ++ "; current token is ~s", [token:format(Token)]).
badtok(_ErrMsg, {error, _Reason} = Error) -> Error;
badtok(ErrMsg, {ok, {Location, Token}}) ->
fmterr(Location, ErrMsg ++ "; current token is ~s", [token:format(Token)]).
fmterr(ScanState, Fmt, Args) ->
{ok, {FileName, LineNr}} = scan_state:location(ScanState),
fmterr({FileName, LineNr}, Fmt, Args) ->
{error, {?MODULE, {FileName, LineNr, Fmt, Args}}}.
-spec format_error(term()) -> io_lib:chars().