diff options
author | Vladimir Azarov <avm@intermediate-node.net> | 2025-05-18 12:07:58 +0200 |
---|---|---|
committer | Vladimir Azarov <avm@intermediate-node.net> | 2025-05-18 12:07:58 +0200 |
commit | 183a4420d2f2a985dd26d76e63c2cdcaafedc5ad (patch) | |
tree | 8fbb929bedf4196aab73a0b630bda38cd58d4cdf | |
parent | 5edd85474d6d8f3a0cc06cc0250ed3db8b26fcfa (diff) |
Conditional inclusion
-rw-r--r-- | common.sml | 12 | ||||
-rw-r--r-- | ppc.fun | 576 | ||||
-rw-r--r-- | ppc.sig | 3 | ||||
-rw-r--r-- | stream.sig | 1 | ||||
-rw-r--r-- | stream.sml | 17 | ||||
-rw-r--r-- | tokenizer.fun | 69 | ||||
-rw-r--r-- | tokenizer.sig | 4 |
7 files changed, 439 insertions, 243 deletions
@@ -108,6 +108,14 @@ fun output stream s = TextIO.output (stream, s) fun fprint stream g = Fold.fold ((false, output stream), fn _ => ()) g fun printf g = fprint TextIO.stdOut g +fun sprintf g = +let + val buf = ref [] + fun output s = (buf := s :: (!buf)) + fun finish _ = String.concat (rev (!buf)) +in + Fold.fold ((false, output), finish) +end g fun Printf output g = Fold.fold ((false, output), fn _ => ()) g local @@ -122,14 +130,14 @@ in (ifF ign (fn () => f (output, v)); (ign, output))) z end -fun Ign z = Fold.step1 (fn (_, (_, output)) => (true, output)) z +fun Ign z = Fold.step0 (fn (_, output) => (true, output)) z fun bind A f = fn z => Fold.fold z A f fun bindWith2str to = bind A1 (fn (output, v) => output (to v)) val I = fn z => bindWith2str Int.toString z val C = fn z => bindWith2str str z -val B = fn z => bindWith2str (fn true => "true" | false => "false") z +val B = fn z => bindWith2str Bool.toString z val R = fn z => bind A1 (fn (output, n) => app (fn f => f ()) (List.tabulate (n, fn _ => fn () => output "\t"))) z @@ -3,22 +3,21 @@ struct structure T = T - type layers = (string * T.S.pos) list - datatype tkPos = TkPos of T.S.pos * layers + type mLayers = (string * T.S.pos) list + datatype tkPos = TkPos of T.S.pos * mLayers - type macroBody = (T.token * T.S.pos) list + type macroBody = (T.token * tkPos) list datatype macro = ObjMacro of macroBody | FuncMacro of string list * macroBody - type t = { - streams: T.S.t list, + datatype layer = Stream of T.S.t | Tokens of (T.token * tkPos) list - buffer: (T.token * tkPos) list, + type t = { + buffer: layer list, macros: (string, bool * macro) Tree.t, - restricted: bool, debugMode: bool, incDirs: string list } @@ -45,8 +44,8 @@ struct val dummyEOSpos = pos2tkPos $ T.S.Pos ("<Unreachable>", 0, 0) - fun raiseTkError msg pos = raise TkError (pos, msg) - fun raiseTkErrorSPos pos msg = raiseTkError msg $ pos2tkPos pos + fun raiseTkError pos msg = raise TkError (pos, msg) + (* fun raiseTkErrorSPos pos = raiseTkError (pos2tkPos pos) *) fun printLayers ((macroName, pos) :: layers) = ( printf `"\t" `macroName `" " T.S.Ppos pos %; @@ -60,7 +59,6 @@ struct ) fun raiseTkClassError pos cls = raise TkClassError (pos, cls) - fun raiseTkClassErrorSPos pos cls = raiseTkClassError (pos2tkPos pos) cls fun tkClassErrorPrint (TkPos (pos, layers), cls) = let @@ -93,18 +91,17 @@ struct val updatePpc = fn z => let - fun from streams buffer macros restricted debugMode incDirs = - { streams, buffer, macros, restricted, debugMode, incDirs } - fun to f { streams, buffer, macros, restricted, debugMode, - incDirs } = - f streams buffer macros restricted debugMode incDirs + fun from buffer macros debugMode incDirs = + { buffer, macros, debugMode, incDirs } + fun to f { buffer, macros, debugMode, incDirs } = + f buffer macros debugMode incDirs in - FRU.makeUpdate6 (from, from, to) + FRU.makeUpdate4 (from, from, to) end z fun create { fname, incDirs, debugMode } = - { streams = [T.S.create fname], buffer = [], macros = Tree.empty, - restricted = false, debugMode, incDirs } + { buffer = [Stream $ T.S.create fname], macros = Tree.empty, debugMode, + incDirs } fun compareLayers cached macroLayers = let @@ -172,30 +169,44 @@ struct (offset2, layers', (fname', line')) end - val printMacroHeader = fn z => + val Players = fn z => let - fun printMacroHeader (out, (id, mLayers)) = + fun Players (out, layers) = let - fun Players (out, layers) = - let - fun printLayer (macro, pos) = - Printf out `macro `" " T.S.Ppos pos % - fun printLayers [] = () - | printLayers [layer] = printLayer layer - | printLayers (layer :: layers) = ( - printLayer layer; - out ", "; - printLayers layers - ) - in - printLayers layers - end - - val layers = rev mLayers + fun printLayer (macro, pos) = + Printf out `macro `" " T.S.Ppos pos % + fun printLayers [] = () + | printLayers [layer] = printLayer layer + | printLayers (layer :: layers) = ( + printLayer layer; + out ", "; + printLayers layers + ) in - Printf out `"\n" `"(" A1 Players layers `"): macro " `id % + printLayers layers end in + bind A1 Players + end z + + val PtkPos = fn z => + let + fun PtkPos (out, TkPos (pos, layers)) = ( + if layers <> [] then + Printf out `"(" Players layers `") " % + else + (); + Printf out T.S.Ppos pos % + ) + in + bind A1 PtkPos + end z + + val printMacroHeader = fn z => + let + fun printMacroHeader (out, (id, mLayers)) = + Printf out `"\n" `"(" Players (rev mLayers) `"): macro " `id % + in bind A1 printMacroHeader end z @@ -220,69 +231,66 @@ struct bind A1 printTokenL end z - datatype IncludeArg = - LocalInc of string * T.S.pos | - ExternalInc of string * T.S.pos + fun updateH head = fn s => Stream head :: tl s - fun parseIncludeArg stream = + fun getTokenNoexpand (P as { buffer = Tokens tks :: _, ... }: t) = ( + case tks of + (tk, pos) :: tail => + (tk, pos, updatePpc P u#buffer (fn buf => Tokens tail :: tl buf) %) + | [] => getTokenNoexpand $ updatePpc P u#buffer tl % + ) + | getTokenNoexpand (P as { buffer = Stream head :: _, ... }: t) = let - fun eat pred stream skipFirst acc = - let - val (c, stream') = T.S.getcharEx stream - in - if pred c then - eat pred stream' skipFirst (c :: acc) - else - (implode o rev $ acc, if skipFirst then stream' else stream) - end - val (_, stream) = eat Char.isSpace stream false [] - val (pos, stream) = T.S.getPos stream - val (start, stream) = T.S.getcharEx stream - val finish = - if start = #"\"" then - #"\"" - else if start = #"<" then - #">" - else - raiseTkErrorSPos pos "expected \" or <" + val (tk, pos, head) = T.getToken head + in + (tk, pos2tkPos pos, updatePpc P u#buffer (updateH head) %) + end + | getTokenNoexpand _ = raise Unreachable + + fun checkClass (tk, pos) clList raiseClassErr = + let + fun belongsToClass tk (Ctk tk') = tk = tk' + | belongsToClass (T.Id _) (Cid) = true + | belongsToClass _ Cid = false + | belongsToClass _ _ = raise Unreachable - val (arg, stream) = eat (fn c => c <> finish) stream true [] - val (_, stream) = - eat (fn c => c = #" " orelse c = #"\t") stream false [] - val (c, stream) = T.S.getcharEx stream + fun checkClass' [] = raiseClassErr pos clList + | checkClass' (cl :: tail) = + if belongsToClass tk cl then + () + else + checkClass' tail in - if c <> #"\n" then - let - val (pos, _) = T.S.getPosAfterChar stream - in - raiseTkErrorSPos pos "expected '\n'" - end - else - (if start = #"\"" then LocalInc (arg, pos) - else ExternalInc (arg, pos), stream) - end handle T.S.EOF => + checkClass' clList + end + + fun getClassGeneric clList getToken raiseClassErr buf = let - val (pos, _) = T.S.EOFpos stream + val (tk, pos, buf) = getToken buf + val () = checkClass (tk, pos) clList raiseClassErr in - raiseTkErrorSPos pos "unexpected EOF during #include argument parsing" + (tk, pos, buf) end - fun findFile arg (stream, incDirs) = + fun getClassNoexpand (ppc: t) clList = + getClassGeneric clList getTokenNoexpand raiseTkClassError ppc + + datatype IncludeArg = LocalInc of string * string | ExternalInc of string + + fun findFile pos arg incDirs = case arg of - LocalInc (arg, pos) => ( + LocalInc (dir, arg) => ( let - val dir = OS.Path.getParent o T.S.getFname $ stream val path = OS.Path.concat (dir, arg) - val (path, instream) = (path, TextIO.openIn path) in (path, instream) end handle - OS.Path.Path => raiseTkErrorSPos pos "invalid argument" - | Size => raiseTkErrorSPos pos "resulting path is too long" + OS.Path.Path => raiseTkError pos "invalid argument" + | Size => raiseTkError pos "resulting path is too long" | IO.Io v => raise IO.Io v ) - | ExternalInc (arg, pos) => + | ExternalInc arg => let fun try (dir :: tail) = ( let @@ -295,149 +303,157 @@ struct in case try incDirs of SOME pair => pair - | NONE => raiseTkErrorSPos pos "unable to find header" + | NONE => raiseTkError pos "unable to find header" end - fun handleInclude (P as { streams = head :: tail, incDirs, ... }: t) = + fun checkEndsWith pos arg c = let - val (arg, oldHead) = parseIncludeArg head - val (path, instream) = findFile arg (head, incDirs) - val head = T.S.createFromInstream path instream + fun find i = + if i = size arg then + raiseTkError pos "unfinished #include argument" + else if String.sub (arg, i) = c then + if i + 1 = size arg then + String.extract (arg, 1, SOME $ size arg - 2) + else + raiseTkError pos "some garbage after #include argument" + else + find (i + 1) in - updatePpc P s#streams (head :: oldHead :: tail) % + find 1 end - | handleInclude _ = raise Unreachable - fun getDefineMacroBody stream acc = + fun parseIncludeArg pos arg dir = let - val (tk, pos, stream) = T.getToken stream - in - case tk of - T.NewLine => (acc, stream) - | T.EOS => raiseTkClassErrorSPos pos [Ctk T.NewLine] - | _ => getDefineMacroBody stream ((tk, pos) :: acc) - end + fun eatSpaces s off = + if off = size s then + raiseTkError pos "invalid #include argument" + else if String.sub (s, off) = #" " then + eatSpaces s (off + 1) + else + String.extract (s, off, NONE) - fun isFuncMacroDefine nameLength (T.S.Pos (_, line1, col1)) stream = - let - val (tk, T.S.Pos (_, line2, col2), _) = T.getToken stream + val arg = eatSpaces arg 0 + val start = String.sub (arg, 0) + val check = checkEndsWith pos arg in - tk = T.LParen andalso line1 = line2 andalso col1 + nameLength = col2 + if start = #"<" then + LocalInc (dir, check #">") + else if start = #"\"" then + ExternalInc $ check #"\"" + else + raiseTkError pos "invalid #include argument" end - fun dBprintf debugMode = - if debugMode then + fun dprintf ppc = + if #debugMode ppc then printf else - printf Ign true - fun dprintf ppc = dBprintf (#debugMode ppc) + printf Ign - fun PrintMacroBody (out, body) = + fun handleInclude (T.PpcInclude (dir, arg), pos) ppc = let - val body = List.map (fn (tk, pos) => (tk, pos2tkPos pos)) body + val arg = parseIncludeArg pos arg dir + val (path, instream) = findFile pos arg (#incDirs ppc) + val () = dprintf ppc `"\n#include: " `path % + val stream = T.S.createFromInstream path instream in - Printf out `" {"; - Printf out printTokenL (0, [], body); - Printf out `"}" % + updatePpc ppc u#buffer (fn buf => Stream stream :: buf) % end + | handleInclude _ _ = raise Unreachable - fun parseDefineObjMacro debugMode stream = + fun getDefineMacroBody ppc acc = let - val (body, stream) = getDefineMacroBody stream [] + val (tk, pos, ppc) = getTokenNoexpand ppc in - dBprintf debugMode A1 PrintMacroBody body; - (ObjMacro body, stream) + case tk of + T.NewLine => (acc, ppc) + | T.EOS => raiseTkClassError pos [Ctk T.NewLine] + | _ => getDefineMacroBody ppc ((tk, pos) :: acc) end - fun checkClass (tk, pos) clList raiseClassErr = + fun isFuncMacroDefine len (TkPos (T.S.Pos (_, line1, col1), [])) ppc = let - fun belongsToClass tk (Ctk tk') = tk = tk' - | belongsToClass (T.Id _) (Cid) = true - | belongsToClass _ Cid = false - | belongsToClass _ _ = raise Unreachable - - fun checkClass' [] = raiseClassErr pos clList - | checkClass' (cl :: tail) = - if belongsToClass tk cl then - () - else - checkClass' tail + val (tk, TkPos (T.S.Pos (_, line2, col2), _), _) = getTokenNoexpand ppc in - checkClass' clList + tk = T.LParen andalso line1 = line2 andalso col1 + len = col2 end + | isFuncMacroDefine _ _ _ = raise Unreachable - fun getClassGeneric clList getToken raiseClassErr buf = + fun PrintMacroBody (out, body) = + if body = [] then + () + else ( + Printf out `" {"; + Printf out printTokenL (0, [], body); + Printf out `"}" % + ) + + fun parseDefineObjMacro ppc = let - val (tk, pos, buf) = getToken buf - val () = checkClass (tk, pos) clList raiseClassErr + val (body, ppc) = getDefineMacroBody ppc [] in - (tk, pos, buf) + dprintf ppc A1 PrintMacroBody body; + (ObjMacro body, ppc) end - fun getClassFromStream stream clList = - getClassGeneric clList T.getToken raiseTkClassErrorSPos stream - fun validateArgs args = let fun validateArg (id, _) [] = id | validateArg (id, pos) ((id', pos') :: tail) = if id = id' then - raiseTkErrorSPos pos' "macro argument name is already taken" + raiseTkError pos' "macro argument name is already taken" else validateArg (id, pos) tail fun validate [] = [] | validate (arg :: args) = validateArg arg args :: validate args - in validate args end - fun parseDefineMacroArgs stream = + fun parseDefineMacroArgs ppc = let - datatype arg = - Arg of string * T.S.pos | - LastArg of string * T.S.pos + datatype arg = Arg of string * tkPos | LastArg of string * tkPos - fun parseArg stream = + fun parseArg ppc = let - val (tkId, posId, stream) = getClassFromStream stream [Cid] - val (tk, _, stream) = getClassFromStream stream [Ctk T.RParen, Ctk T.Coma] + val (tkId, posId, ppc) = getClassNoexpand ppc [Cid] + val (tk, _, ppc) = getClassNoexpand ppc [Ctk T.RParen, Ctk T.Coma] val id = case tkId of T.Id id => id | _ => raise Unreachable in case tk of - T.RParen => (LastArg (id, posId), stream) - | T.Coma => (Arg (id, posId), stream) + T.RParen => (LastArg (id, posId), ppc) + | T.Coma => (Arg (id, posId), ppc) | _ => raise Unreachable end - fun parseArgs stream = + fun parseArgs ppc = let - val (tk, _, stream) = T.getToken stream + val (tk, _, ppc) = getTokenNoexpand ppc - fun parse stream acc = - case parseArg stream of - (LastArg p, stream) => (rev (p :: acc), stream) - | (Arg p, stream) => parse stream (p :: acc) + fun parse ppc acc = + case parseArg ppc of + (LastArg p, ppc) => (rev (p :: acc), ppc) + | (Arg p, ppc) => parse ppc (p :: acc) in if tk = T.RParen then - ([], stream) + ([], ppc) else let - val (args, stream) = parse stream [] + val (args, ppc) = parse ppc [] val args = validateArgs args in - (args, stream) + (args, ppc) end end in - parseArgs stream + parseArgs ppc end - fun parseDefineFuncMacro debugMode stream = + fun parseDefineFuncMacro ppc = let - val (params, stream) = parseDefineMacroArgs stream - val (body, stream) = getDefineMacroBody stream [] + val (params, ppc) = parseDefineMacroArgs ppc + val (body, ppc) = getDefineMacroBody ppc [] fun printParams out = let @@ -450,58 +466,58 @@ struct Printf out `")" % end in - dBprintf debugMode A0 printParams; - dBprintf debugMode A1 PrintMacroBody body; - (FuncMacro (params, rev body), stream) + dprintf ppc A0 printParams; + dprintf ppc A1 PrintMacroBody body; + (FuncMacro (params, rev body), ppc) end - fun parseDefine stream debugMode = + fun parseDefine ppc = let - val (macroName, pos, stream) = getClassFromStream stream [Cid] + val (macroName, pos, ppc) = getClassNoexpand ppc [Cid] val macroName = case macroName of T.Id id => id | _ => raise Unreachable - val () = dBprintf debugMode `"\ndefine " `macroName % + val () = dprintf ppc `"\ndefine " `macroName % val parser = - if isFuncMacroDefine (size macroName) pos stream then + if isFuncMacroDefine (size macroName) pos ppc then parseDefineFuncMacro else parseDefineObjMacro - val (macro, stream) = parser debugMode stream + val (macro, ppc) = parser ppc in - ((macroName, pos), macro, stream) + ((macroName, pos), macro, ppc) end - fun updateH head = fn s => head :: tl s - - fun handleDefine (P as { streams = head :: _, ... }: t) = + fun handleDefine _ ppc = let - val ((macroName, pos), macro, head) = parseDefine head (#debugMode P) + val ((macroName, pos), macro, ppc) = parseDefine ppc - val macros = insertMacro (#macros P) macroName (false, macro) handle - Tree.Exists => raiseTkErrorSPos pos "macro redefinition" + val macros = insertMacro (#macros ppc) macroName (false, macro) + handle Tree.Exists => raiseTkError pos "macro redefinition" in - updatePpc P u#streams (updateH head) s#macros macros % + updatePpc ppc s#macros macros % end - | handleDefine _ = raise Unreachable - - val directiveTable = [ - (T.PpcInclude, handleInclude), - (T.PpcDefine, handleDefine) - ] fun addLayer (id, TkPos (pos, layers)) = (id, pos) :: layers - fun addLayers idPos body = + + fun setLayers idPos body = let - fun formLayers (tk, pos) = (tk, TkPos (pos, addLayer idPos)) + fun formLayers (tk, TkPos (pos, _)) = (tk, TkPos (pos, addLayer idPos)) in List.map formLayers body end fun insertRevBody body ppc = - updatePpc ppc u#buffer (fn buf => List.revAppend (body, buf)) % + let + fun f (B as (Stream _ :: _)) = Tokens (rev body) :: B + | f (Tokens tks :: tail) = + Tokens (List.revAppend (body, tks)) :: tail + | f _ = raise Unreachable + in + updatePpc ppc u#buffer f % + end val printBody = fn z => let @@ -516,12 +532,10 @@ struct fun expandObjMacro (id, pos) body ppc = let - val TkPos (pos', _) = pos - val mend = (T.MacroEnd id, if body = [] then pos' else (#2 o hd) body) - val revBody = addLayers (id, pos) $ List.concat [[mend], body] + val mend = (T.MacroEnd id, if body = [] then pos else (#2 o hd) body) + val revBody = setLayers (id, pos) $ List.concat [[mend], body] val mLayers = addLayer (id, pos) - in dprintf ppc printMacroHeader (id, mLayers); dprintf ppc printBody (mLayers, "body", rev revBody); @@ -533,12 +547,23 @@ struct and parseFuncMacroArgs mPos params ppc = let + + fun getTokenRestricted ppc = + let + val (tk, pos, ppc) = getTokenNoexpand ppc + in + if T.isPpcDir tk then + raiseTkError pos "preprocessor directive inside macro arguments" + else + (tk, pos, ppc) + end + fun parseArg ppc acc = let - val (tk, pos, ppc) = getToken ppc (* TODO: should be restricted *) + val (tk, pos, ppc) = getTokenRestricted ppc in case tk of - T.EOS => raiseTkError "unfinished argument list" mPos + T.EOS => raiseTkError mPos "unfinished argument list" | T.Coma => (true, rev acc, ppc) | T.RParen => (false, rev acc, ppc) | _ => parseArg ppc ((tk, pos) :: acc) @@ -546,7 +571,7 @@ struct fun parseArgs ppc params acc = let - fun bind _ [] = raiseTkError "too many arguments" mPos + fun bind _ [] = raiseTkError mPos "too many arguments" | bind body (param :: params) = ((param, body), params) val (continue, arg, ppc) = parseArg ppc [] @@ -556,22 +581,21 @@ struct parseArgs ppc otherParams (bindedParam :: acc) else if length otherParams > 0 then - raiseTkError "not enough arguments" mPos + raiseTkError mPos "not enough arguments" else (rev (bindedParam :: acc), ppc) end - val ppc = updatePpc ppc s#restricted true % - val (_, _, ppc) = getClass ppc [Ctk T.LParen] + val (_, _, ppc) = getClassGeneric [Ctk T.LParen] + getTokenRestricted raiseTkClassError ppc val (res, ppc) = parseArgs ppc params [] - val ppc = updatePpc ppc s#restricted false % in (res, ppc) end and expandArgument ppc arg = let - val ppc = updatePpc ppc s#streams [] s#buffer arg % + val ppc = updatePpc ppc s#buffer [Tokens arg] % fun getAll ppc acc = let val (tk, pos, ppc) = getToken ppc @@ -641,20 +665,103 @@ struct val bp2 = apply (expandArgument ppc) bp1 val () = dprintf ppc printBinded (mLayers, "expanded args", bp2) % - val body = addLayers (id, mPos) body + val body = setLayers (id, mPos) body val body = subst bp2 body [] in dprintf ppc printBody (mLayers, "subst", body); insertRevBody (rev body) ppc end + and getIfRevBody ifPos cond (ppc: t) = + let + fun collect level f acc ppc = + let + val (tk, pos, ppc) = getTokenNoexpand ppc + fun def dx = collect (level + dx) f (f ((tk, pos), acc)) ppc + in + case tk of + T.PpcEndif => + if level = 0 then + let + val (_, _, ppc) = getClassNoexpand ppc [Ctk T.NewLine] + in + (acc, ppc) + end + else + def (~1) + | T.EOS => raiseTkError ifPos "unfinished conditional directive" + | _ => + if tk = T.PpcIf orelse tk = T.PpcIfdef + orelse tk = T.PpcIfndef + then + def 1 + else + def 0 + end + + val skip = collect 0 (fn (_, _) => []) [] + val collect = collect 0 (op ::) [] + in + (if cond then collect else skip) ppc + end + + and ifdefEval pos ifPos ppc = + let + fun isDefined id = + case Tree.lookup macroCompare (#macros ppc) id of + SOME _ => true + | _ => false + + val (macro, _, ppc) = getClassNoexpand ppc [Cid] + val id = case macro of T.Id id => id | _ => raise Unreachable + val (_, _, ppc) = getClassNoexpand ppc [Ctk T.NewLine] + + val defined = isDefined id + val (cond, form) = + if pos then + (defined, "ifdef") + else + (not defined, "ifndef") + in + dprintf ppc `"\n" PtkPos ifPos `": #" `form `" " `id `" -> " B cond %; + (cond, ppc) + end + + and ifEval ifPos ppc = + let + fun skip ppc = + let + val (tk, _, ppc) = getTokenNoexpand ppc + in + case tk of + T.EOS => raiseTkError ifPos "unfinished #if condition" + | T.NewLine => ppc + | _ => skip ppc + end + in + (true, skip ppc) + end + + and handleIf (tk, ifPos) ppc = + let + val dprintf = dprintf ppc `"\n" + val (cond, ppc) = + (case tk of + T.PpcIfdef => ifdefEval true + | T.PpcIfndef => ifdefEval false + | _ => ifEval) ifPos ppc + + val (revBody, ppc) = getIfRevBody ifPos cond ppc + in + dprintf `"{" printTokenL (1, [], rev revBody) `"}\n"; + insertRevBody revBody ppc + end + and handleRegularToken tk pos ppc = let fun checkAndMark (true, _) = (NONE, NONE) | checkAndMark (false, macro) = (SOME (true, macro), SOME macro) - fun getMacro tree id = macrosLookup tree id (checkAndMark, NONE) - fun def () = (tk, pos, ppc) fun handleMacro id = @@ -671,7 +778,7 @@ struct end in case tk of - T.Id id => if (#restricted ppc) then def () else handleMacro id + T.Id id => handleMacro id | T.MacroEnd id => let val f = fn (_, macro) => (SOME (false, macro), ()) @@ -682,40 +789,55 @@ struct | _ => def () end - and handleToken tk pos ppc = - let - fun checkMode () = - if (#restricted ppc) then - raiseTkError "directive in unexpected place" pos - else - ppc - in - case List.find (fn (tk', _) => tk' = tk) directiveTable of - SOME (_, f) => getToken o f $ checkMode () + and ppcFallback (_, pos) _ = + raiseTkError pos "directive is not implemented" + + and handleToken tk pos (ppc: t) = + let + fun %tk = fn tk' => tk' = tk + val directiveTable = [ + (fn T.PpcInclude _ => true | _ => false, handleInclude), + (%T.PpcDefine, handleDefine), + (%T.PpcIfdef, handleIf), + (%T.PpcIfndef, handleIf), + (%T.PpcUndef, ppcFallback), + (%T.PpcIf, handleIf), + (%T.PpcElse, ppcFallback), + (%T.PpcElif, ppcFallback), + (%T.PpcEndif, ppcFallback), + (%T.PpcWarning, ppcFallback), + (%T.PpcError, ppcFallback), + (%T.PpcPragma, ppcFallback) + ] + in + case List.find (fn (f, _) => f tk) directiveTable of + SOME (_, f) => getToken $ f (tk, pos) ppc | NONE => handleRegularToken tk pos ppc end - and getToken (P as { buffer = (tk, pos) :: tail, ... }: t) = - handleToken tk pos (updatePpc P s#buffer tail %) - | getToken (P as { streams = [], ... }: t) = (T.EOS, dummyEOSpos, P) - | getToken (P as { streams = head :: tail, restricted, ... }: t) = + and getToken (P as { buffer = Tokens tks :: _, ... }: t) = ( + case tks of + (tk, pos) :: tail => + handleToken tk pos + (updatePpc P u#buffer (fn b => Tokens tail :: tl b) %) + | [] => getToken $ updatePpc P u#buffer tl % + ) + | getToken (P as { buffer = [], ... }: t) = (T.EOS, dummyEOSpos, P) + | getToken (P as { buffer = Stream head :: tail, ... }: t) = let val (tk, pos, head) = T.getToken head in - if tk = T.EOS then - case (restricted, tail) of - (true, _) => - (T.EOS, dummyEOSpos, updatePpc P u#streams (updateH head) %) - | (false, []) => - let - val (pos, head) = T.S.EOFpos head - in - (T.EOS, pos2tkPos pos, updatePpc P s#streams [head] %) - end - | _ => getToken $ updatePpc P s#streams tail % - else + case (tk, tail) of + (T.EOS, []) => + let + val (pos, head) = T.S.EOFpos head + in + (T.EOS, pos2tkPos pos, updatePpc P s#buffer [Stream head] %) + end + | (T.EOS, tail) => getToken $ updatePpc P s#buffer tail % + | (_, _) => handleToken tk (pos2tkPos pos) $ - updatePpc P u#streams (updateH head) % + updatePpc P u#buffer (updateH head) % end fun debugPrint' cache ppc = @@ -22,8 +22,9 @@ signature PPC = sig val create: { fname: string, incDirs: string list, debugMode: bool } -> t val debugPrint: string -> string list -> unit + val getClass: t -> tkClass list -> T.token * tkPos * t - val raiseTkError: string -> tkPos -> 'a + val raiseTkError: tkPos -> string -> 'a val tkErrorPrint: tkErrorVal -> unit val raiseTkClassError: tkPos -> tkClass list -> 'a @@ -20,6 +20,7 @@ signature STREAM = sig val EOFpos: t -> pos * t val getSubstr: fileOffset -> fileOffset -> t -> string + val getLine: t -> string option * t val getFname: t -> string (* both throw IO.Io *) @@ -52,6 +52,23 @@ structure Stream :> STREAM = struct fun getSubstr startOff endOff ({ contents, ... }: t) = String.substring (contents, startOff, endOff - startOff) + fun getLine (S as { contents, off, ... }: t) = + let + fun find off = + if off = size contents then + NONE + else + if String.sub (contents, off) = #"\n" then + SOME off + else + find (off + 1) + in + case find off of + SOME off' => + (SOME $ getSubstr off off' S, updateStream S s#off off' %) + | NONE => (NONE, S) + end + fun getFname ({ fname, ... }: t) = fname fun createFromInstream fname instream = diff --git a/tokenizer.fun b/tokenizer.fun index 9d7f8fc..4bdc047 100644 --- a/tokenizer.fun +++ b/tokenizer.fun @@ -16,7 +16,6 @@ struct Invalid | EOS | NewLine | - MacroStart of string | MacroEnd of string | Num of numConst | @@ -114,7 +113,7 @@ struct CommentStart | - PpcInclude | + PpcInclude of string * string | PpcDefine | PpcUndef | PpcIf | @@ -132,8 +131,6 @@ struct exception TkError of tkErrorAuxInfo * string exception TkErrorAug of S.pos * string - exception ExpectedPpcDir (* handled in postprocess *) - exception FsmTableIsTooSmall (* Unreachable (should be) *) @@ -244,7 +241,6 @@ struct (CommentStart, "/*"), - (PpcInclude, %"include"), (PpcDefine, %"define"), (PpcUndef, %"undef"), (PpcIf, %"if"), @@ -285,8 +281,9 @@ struct fun printToken (out, tk) = case tk of Id s => Printf out `s % - | MacroStart macro => Printf out `"m(" `macro `")" % | MacroEnd macro => Printf out `"mend(" `macro `")" % + | PpcInclude (dir, arg) => + Printf out `"#include(" `dir `", " `arg `")" % | Num (IntConst (it, str, sfx)) => let val intType = @@ -861,7 +858,53 @@ struct val charParser = seqParser SpmChr val strParser = seqParser SpmStr - fun formPpcDir (Id s) = + fun getDir stream = OS.Path.getParent o S.getFname $ stream + + fun completePpcInclude (S.Pos (fname, line, _)) stream = + let + val pos = S.Pos (fname, line, 1) + val (line, stream) = S.getLine stream + in + case line of + SOME line => (PpcInclude (getDir stream, line), stream) + | NONE => raise TkErrorAug (pos, "line does not end with '\n'\n") + end + + fun isPpcDir (PpcInclude _) = true + | isPpcDir tk = + case List.find (fn (tk', _) => tk' = tk) tokenRepr of + SOME (_, repr) => String.sub (repr, 0) = ppcPrefix + | NONE => false + + fun handlePpcDir (tk, pos) stream = + let + open String + fun error () = + raise TkErrorAug (pos, "expected preprocessor directive") + + fun getById id = + let + fun right repr = + sub (repr, 0) = ppcPrefix andalso extract (repr, 1, NONE) = id + in + case List.find (fn (_, repr) => right repr) tokenRepr of + SOME (tk, _) => (tk, stream) + | NONE => + if id = "include" then + completePpcInclude pos stream + else + error () + end + in + case tk of + Id id => getById id + | kwElse => (PpcElse, stream) + | kwIf => (PpcIf, stream) + | _ => error () + end + + (* + fun formPpcDir (Id s) = let open String in @@ -878,10 +921,10 @@ struct | formPpcDir kwIf = PpcIf | formPpcDir _ = raise ExpectedPpcDir - fun handlePpcDir (pos, tk) = - formPpcDir tk handle - ExpectedPpcDir => + fun handlePpcDir (pos, tk) stream = + formPpcDir tk stream handle ExpectedPpcDir => raise TkErrorAug (pos, "expected preprocessor directive") + *) fun unexpectedCharRaise stream c = let @@ -949,7 +992,11 @@ struct if tk = EOS then raise TkErrorAug (pos, "unfinished preprecessor directive") else - (handlePpcDir (pos', tk), pos, stream) + let + val (tk, stream) = handlePpcDir (tk, pos') stream + in + (tk, pos, stream) + end end else (tk, pos, stream) diff --git a/tokenizer.sig b/tokenizer.sig index 53a9f17..a0f5127 100644 --- a/tokenizer.sig +++ b/tokenizer.sig @@ -13,7 +13,6 @@ signature TOKENIZER = sig Invalid | EOS | NewLine | - MacroStart of string | MacroEnd of string | Num of numConst | @@ -111,7 +110,7 @@ signature TOKENIZER = sig CommentStart | - PpcInclude | + PpcInclude of string * string | PpcDefine | PpcUndef | PpcIf | @@ -131,5 +130,6 @@ signature TOKENIZER = sig val getToken: S.t -> token * S.pos * S.t val Ptoken: (token, 'a, 'b) a1printer + val isPpcDir: token -> bool val debugPrint: string -> unit end |