ntg-context - mailing list for ConTeXt users
 help / color / mirror / Atom feed
* Verbatim: Pascal pretty printer
@ 2010-10-02 21:10 Stefan Müller
  0 siblings, 0 replies; only message in thread
From: Stefan Müller @ 2010-10-02 21:10 UTC (permalink / raw)
  To: mailing list for ConTeXt users

[-- Attachment #1: Type: text/plain, Size: 1006 bytes --]

Hi list,

I wrote a custom pretty printer (verbatim) for Free Pascal/Turbo 
Pascal/Delphi source files. It's attached to this mail together with a 
ConTeXt test files that shows the features and some special cases.

The pretty printer can handle:
reserved words (bold)
{...}-, (*...*)- and //...-style comments
{$...} compiler directives
'...' strings
embedded assembler code (asm ... end)

It's based on the Lazarus IDE syntax highlighter (Free Pascal IDE), 
which should be pretty Delphi-like. The main difference to Lazarus 
highlighting is that comments are not bold, but slanted. That looks much 
better when no colors are used.

I'm not experienced in Lua programming, but I hope there are no major 
issues. I'd really appreciate any feedback: comments, suggestions and 
corrections (if necessary). I hope that eventually the Pascal pretty 
printer can make it's way into the ConTeXt distribution.

Best wishes,
Stefan.

PS: Thanks for the Lua pretty printer, which served as a good starting 
point.

[-- Attachment #2: pret-pas.lua --]
[-- Type: text/plain, Size: 7788 bytes --]

if not modules then modules = { } end modules ['pret-pas'] = {
    version   = 1.0,
    comment   = "custom pretty printer for Pascal code",
    author    = "Stefan Müller, Chemnitz DE",
    copyright = "Stefan Müller",
    license   = "see context related readme files"
}

-- The code formatting is adapted from Lazarus, a Free Pascal RAD IDE, which
-- should be quite similar to the Delphi style. Please note that comments are
-- not typeset with bold fontface but slanted, which improves output when not
-- using colors.
-- http://lazarus.freepascal.org/

local visualizer = buffers.newvisualizer("pas")

-- The list of reserved words is taken from 
-- http://www.freepascal.org/docs-html/ref/refse3.html
visualizer.reservedwords = {
	-- Turbo Pascal
	"absolute", "and", "array", "asm", "begin", "case", "const", "constructor",
	"destructor", "div", "do", "downto", "else", "end", "file", "for",
	"function", "goto", "if", "implementation", "in", "inherited", "inline",
	"interface", "label", "mod", "nil", "not", "object", "of", "on", "operator",
	"or", "packed", "procedure", "program", "record", "reintroduce", "repeat",
	"self", "set", "shl", "shr", "string", "then", "to", "type", "unit",
	"until", "uses", "var", "while", "with", "xor",

	-- Free Pascal
	-- these are not bold type (keeping them, just in case)
	-- "dispose", "exit", "false", "new", "true",

	-- Object Pascal
    "as", "class", "dispinterface", "except", "exports", "finalization",
	"finally", "initialization", "inline", "is", "library", "on", "out",
	"packed", "property", "raise", "resourcestring", "threadvar", "try",

	-- Modifiers
	-- some of these are only bold in specific places (in the following, this is
	-- deliberately ignored)
	"absolute", "abstract", "alias", "assembler", "cdecl", "cppdecl", "default",
	"export", "external", "far", "far16", "forward", "index", "local", "name", 
	"near", "nostackframe", "oldfpccall", "override", "pascal", "private",
	"protected", "public", "published", "read", "register", "reintroduce",
	"safecall", "softfloat", "stdcall", "virtual", "write"
}

local known_words = { }

for k,v in next, visualizer.reservedwords do
    known_words[v] = k
end

local colors = {
    "prettyone", -- red: compiler directive, symbol
    "prettytwo", -- green: assembler (dark green)
    "prettythree", -- blue: comment, number (dark blue)
    "prettyfour", -- yellow
}

local reserved_style = "\{\\bf "
local comment_style = "\{\\sl "

local inlongcomment, inlongcomment_alt, incompdirec, inasm = false, 0, false, false

local function flush_pas_word(word, state)
    if word then
		local lword = string.lower(word)
        local id = known_words[lword]
        if id then
			if inasm and (lword == "end") then
				-- asm mode ends
				state = buffers.finishstate(state)
				inasm = false
				print("leave asm")
			end
			if not inasm then
				tex.sprint(tex.ctxcatcodes, reserved_style)
				tex.write(word)
				tex.sprint(tex.ctxcatcodes, "\}")
				if lword == "asm" then
					-- asm mode begins
					print("enter asm")
					inasm = true
					state = buffers.changestate(2, state)
				end
			else
				tex.write(word)
			end
        else
            tex.write(word)
        end
    end
	return state
end

local function flush_whatever(str)
	if str then
		for c in string.utfcharacters(str) do
			if c == " " then
				tex.sprint(tex.ctxcatcodes, "\\obs")
			elseif c == "\t" then
				tex.sprint(tex.ctxcatcodes, "\\obs")
				if buffers.visualizers.enabletab then
					tex.sprint(tex.ctxcatcodes,rep("\\obs ", buffers.visualizers.tablength))
				end
			else
				tex.write(c);
			end
		end
	end
end

function visualizer.reset()
    inlongcomment, inlongcomment_alt, incompdirec, inasm = false, 0, false, false
end

function visualizer.flush_line(str, nested)
    local state = 0
	local incomment, instring = false, false
	--local code, comment = nil, nil
    buffers.currentcolors = colors

	if inlongcomment or (inlongcomment_alt == 2) or incompdirec then
		incomment = true
		if incompdirec then
			state = buffers.changestate(1, state)
		else
			state = buffers.changestate(3, state)
		end
		tex.sprint(tex.ctxcatcodes, comment_style)
	elseif inasm then
		state = buffers.changestate(2, state)
	end
	
	local c, word = nil, nil
	for nextc in string.utfcharacters(str .. " ") do
		if c then
			if instring then
				if c == "'" then
					-- string ends
					tex.write(c)
					state = buffers.finishstate(state)
					instring = false
				else
					-- inside the string
					flush_whatever(c)
				end
			elseif incomment then
				if ((inlongcomment or incompdirec) and (c == "}"))
				or (inlongcomment_alt == 1) then
					-- long comment/(alternative)/compiler directive ends
					tex.write(c)
					tex.sprint(tex.ctxcatcodes, "\}")
					if inasm then
						-- resume to asm mode
						state = buffers.changestate(2, state)
					else
						state = buffers.finishstate(state)
					end
					incompdirec = false
					inlongcomment = false
					inlongcomment_alt = 0
					incomment = false
				elseif (inlongcomment_alt == 2) and (c == "*") and (nextc == ")") then
					-- long comment (alternative) ends after nextc
					tex.write(c)
					inlongcomment_alt = 1
				else
					-- inside the comment
					flush_whatever(c)
				end
			elseif string.find(c, "^[%a_]$") then
				-- char belongs to identifier
				if word then
					word = word .. c
				else
					word = c
				end
			elseif string.find(c, "^[%d]$") then
				if word and (#word > 1) then
					-- number, that belongs to identifier
					word = word .. c
				else
					if not inasm then
						-- number
						state = buffers.changestate(3, state)
					end
					tex.write(c)
				end
			else
				if not inasm then
					state = buffers.finishstate(state)
				end
				-- identifier complete, check if it's a reserved word and flush
				state = flush_pas_word(word, state)
				word = nil
				if c == " " then
					tex.sprint(tex.ctxcatcodes, "\\obs")
				elseif c == "\t" then
					tex.sprint(tex.ctxcatcodes, "\\obs")
					if buffers.visualizers.enabletab then
						tex.sprint(tex.ctxcatcodes,rep("\\obs ", buffers.visualizers.tablength))
					end
				elseif c == "'" then
					if not inasm then
						-- string begins
						instring = true
						state = buffers.changestate(3, state)
					end
					tex.write(c)
				elseif (c == "/") and (nextc == "/") then
					-- one-line comment begins
					incomment = true
					state = buffers.changestate(3, state)
					tex.sprint(tex.ctxcatcodes, comment_style)
					tex.write(c)
				elseif c == "{" then
					incomment = true
					if nextc == "$" then
						-- compiler directive begins
						incompdirec = true
						state = buffers.changestate(1, state)
					else
						-- long comment begins
						inlongcomment = true
						state = buffers.changestate(3, state)
					end
					tex.sprint(tex.ctxcatcodes, comment_style)
					tex.write(c)
				elseif (c == "(") and (nextc == "*") then 
					-- long comment (alternative) begins
					incomment = true
					inlongcomment_alt = 2
					state = buffers.changestate(3, state)
					tex.sprint(tex.ctxcatcodes, comment_style)
					tex.write(c)
				else
					if not inasm then
						-- symbol
						state = buffers.changestate(1, state)
					end
					tex.write(c)
				end
			end
		end
		c = nextc
	end
	if not incomment and not inasm then
		state = buffers.finishstate(state)
	end
	-- maybe something left, check if it's a reserved word and flush
	state = flush_pas_word(word, state)
	if incomment then
		-- end the comment-line
		tex.sprint(tex.ctxcatcodes, "\}")
	end
	state = buffers.finishstate(state)	
end

[-- Attachment #3: pret-pas_test.tex --]
[-- Type: application/x-tex, Size: 1276 bytes --]

[-- Attachment #4: Type: text/plain, Size: 486 bytes --]

___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!

maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage  : http://www.pragma-ade.nl / http://tex.aanhet.net
archive  : http://foundry.supelec.fr/projects/contextrev/
wiki     : http://contextgarden.net
___________________________________________________________________________________

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2010-10-02 21:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-02 21:10 Verbatim: Pascal pretty printer Stefan Müller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).