Seems I managed to convince it to work.
>>> a = ['Some', 'test']
>>> ', '.join(a)
'Some, test'
Monday, May 18, 2009
Test for the syntax highlighter
The point of this post is to test the syntax highlighter.
Gedit colorization
So, here's a gedit text colorization snippet.
Start with an empty document and open the bottom pane, with the Python console activated (View|Bottom Pane, Edit|Preferences|Plugins).
Enter this code:
Nothing happens, we need to change the colors associated with the tag. It looks like I need to install some kind of support for inserting source code in blog posts. Tomorrow I will try these suggestions.
Tada! The text is now colorized. Since I'm selfish and sleepy, I won't go into details about the functions that were used (this post is mainly a self-note of how to perform text colorization). If anyone reads this and you feel like you could use some advice, try:
'dir'ring python objects is helpful at times. Still, it would be better if the gedit/gtksourceview guys provided more documentation.
Good night to myself.
Start with an empty document and open the bottom pane, with the Python console activated (View|Bottom Pane, Edit|Preferences|Plugins).
Enter this code:
>>> v = window.get_active_view()
>>> b = v.get_buffer()
>>> t = b.create_tag("simple_tag")
>>> b.insert(b.get_start_iter(), "hello")
>>> b.apply_tag(t, b.get_start_iter(), b.get_end_iter())
Nothing happens, we need to change the colors associated with the tag. It looks like I need to install some kind of support for inserting source code in blog posts. Tomorrow I will try these suggestions.
>>> import gtk
>>> t.props.foreground_gdk = gtk.gdk.Color("#fa0")
Tada! The text is now colorized. Since I'm selfish and sleepy, I won't go into details about the functions that were used (this post is mainly a self-note of how to perform text colorization). If anyone reads this and you feel like you could use some advice, try:
>>> dir(b)
'dir'ring python objects is helpful at times. Still, it would be better if the gedit/gtksourceview guys provided more documentation.
Good night to myself.
Resurrection... again
So I tried working on this project last year... and I I failed. Or at list I didn't get what I wanted, just a better understanding of monads and with that the conclusion that I don't like working with them.
It seems the old synpl project is like a fungus, it never dies if left alone in dark corners with high humidity.
I'm going to try again. This time, no super PL/SQL editor, no C compiler... but a structured editor.
Why? I'm very impressed with the usability of org-mode in Emacs and I want that kind of functionality (moving around content as if it was a tree) in other editing tasks.
I guess this experiment will die an unspectacular death like the previous ones. Well, if that's what it takes to keep the project alive...
Enough rambling. Time to actually use this blog space for something practical (and maybe useful to others).
Since I am not completely crazy, I don't want to implement an editor from scratch. I also don't want to use emacs (somehow elisp seems hard to use for large projects). I will use... gedit.
Yes, the puny notepad like editor distributed with Gnome.
Actually, it turns out that it is a very nice editor. For my purposes, at least. Because it has all the features I want (stylable editor, an API - in Python and explorable with a REPL, no less).
So my learning tasks are now these:
It seems the old synpl project is like a fungus, it never dies if left alone in dark corners with high humidity.
I'm going to try again. This time, no super PL/SQL editor, no C compiler... but a structured editor.
Why? I'm very impressed with the usability of org-mode in Emacs and I want that kind of functionality (moving around content as if it was a tree) in other editing tasks.
I guess this experiment will die an unspectacular death like the previous ones. Well, if that's what it takes to keep the project alive...
Enough rambling. Time to actually use this blog space for something practical (and maybe useful to others).
Since I am not completely crazy, I don't want to implement an editor from scratch. I also don't want to use emacs (somehow elisp seems hard to use for large projects). I will use... gedit.
Yes, the puny notepad like editor distributed with Gnome.
Actually, it turns out that it is a very nice editor. For my purposes, at least. Because it has all the features I want (stylable editor, an API - in Python and explorable with a REPL, no less).
So my learning tasks are now these:
- Learn the Gedit API.
- Write an editor wrapper lib for the functionality I need (so I can switch to IronPython and ScintillaNET if I want to port my little scripts to Windows).
- Write a Scheme/Lisp parser (or use an existing one) and test the usability of the structured approach.
- Keep going with a JavaScript parser and structured editor.
- Extend the JavaScript parser to support QooxDoo and project files (discovered automatically from exploring the file system neighborhood of the current file).
- ... hmmm, profit? (mentally, at least)
- Get the cursor position.
- Set the cursor position.
- Get the current selection.
- Set the current selection.
- Change text.
- Colorize text.
- Handle keyboard shortcuts.
Wednesday, July 16, 2008
Resurrection attempt...
Hmmm... It looks like I've abandoned this project for almost one year.
Since I'm trying to learn Haskell and functional languages are supposed to be really good at handling compiling/translating/transforming tasks, it may be a good idea to try to reimplement things in Haskell. This and reading 'Real world Haskell' should complement nicely.
Here's a first attempt: a simple lexer written in less than 100 lines of Haskell (though it's pretty 'high density', without any comments).
A few tasks that should help me become a better Haskell programmer:
Until then, here's my newbie Haskeller code (uncommented :-( ):
Since I'm trying to learn Haskell and functional languages are supposed to be really good at handling compiling/translating/transforming tasks, it may be a good idea to try to reimplement things in Haskell. This and reading 'Real world Haskell' should complement nicely.
Here's a first attempt: a simple lexer written in less than 100 lines of Haskell (though it's pretty 'high density', without any comments).
A few tasks that should help me become a better Haskell programmer:
- document things (the literate Haskell mode works great with Emacs, no excuses this time); add examples of usage in the comments !
- learn how to isolate the lexer guts by using the Haskell module system
- use the State monad, because there is state passed around in the lexer (although in a rather uniform manner, which avoids 'ladders to the right of the screen')
- maybe use the Reader monad (there are some configuration items, such as the width of a tab)
- ... so maybe learn how to use monad transformers to 'stack' State and Reader?
Until then, here's my newbie Haskeller code (uncommented :-( ):
> data StreamPosition = StreamPosition { line :: Int, column :: Int }
> deriving Show
> data TokenPosition = TokenPosition { start :: StreamPosition,
> end :: StreamPosition }
> deriving Show
> data TokenContent = TokenContent { position :: TokenPosition,
> content :: String }
> deriving Show
> data Token = TokenNumber TokenContent
> | TokenIdentifier TokenContent
> | TokenChar TokenContent
> deriving Show
> data LexerState = LexerState StreamPosition String [Token]
> lex :: String -> [Token]
> lex input = lexImpl $ LexerState (StreamPosition 1 1) input []
> where lexImpl (LexerState pos [] tokens) = reverse tokens
> lexImpl arg@(LexerState pos input@(c:cs) tokens)
> | c == '\n' = lexImpl $ LexerState (addToLine pos 1) cs tokens
> | c == ' ' = lexImpl $ LexerState (addToColumn pos 1) cs tokens
> | c == '\n' = lexImpl $ LexerState (addToColumn pos 8) cs tokens
> | isDigit c = lexImpl $ lexNumber arg
> | isLetterOrUnderscore c = lexImpl $ lexIdentifier arg
> | otherwise = lexImpl $ LexerState (addToColumn pos 1) cs upTokens
> where upTokens = (charTok : tokens)
> tokenPos = TokenPosition pos pos
> charTok = TokenChar $ TokenContent tokenPos [c]
> isDigit c = c >= '0' && c <= '9'
> isLetterOrUnderscore c = c == '_' || isLower || isUpper
> where isLower = c >= 'a' && c <= 'z'
> isUpper = c >= 'A' && c <= 'Z'
> lexNumber :: LexerState -> LexerState
> lexNumber = lexEntity isDigit TokenNumber
> lexIdentifier = lexEntity isLetterOrUnderscore TokenIdentifier
> lexEntity :: (Char -> Bool) -> (TokenContent -> Token) -> LexerState -> LexerState
> lexEntity spanP tokenCtor (LexerState pos cs tokens) = LexerState upPos upCs upTokens
> where (content, upCs) = span spanP cs
> upPos = addToColumn pos (length content)
> tokenPos = TokenPosition pos (addToColumn upPos (-1))
> numTok = tokenCtor (TokenContent tokenPos content)
> upTokens = (numTok : tokens)
> addToLine (StreamPosition line column) lineInc = StreamPosition (line + lineInc) column
> addToColumn (StreamPosition line column) columnInc =
> StreamPosition line (column + columnInc)
> data TokenTransform = TokenTransform { predicate :: ([Token] -> Int),
> combiner :: ([Token] -> [Token]) }
> applyTokenTransform :: [Token] -> TokenTransform -> [Token]
> applyTokenTransform [] _ = []
> applyTokenTransform tokens transform@(TokenTransform p c) =
> let matchLen = p tokens
> in if matchLen > 0
> then let (theMatch, theRest) = splitAt matchLen tokens
> theRest' = applyTokenTransform theRest transform
> in (c theMatch) ++ theRest'
> else let theHead = head tokens
> theRest = tail tokens
> in (theHead : applyTokenTransform theRest transform)
> applyTokenTransforms :: [Token] -> [TokenTransform] -> [Token]
> applyTokenTransforms tokens (t:ts) = applyTokenTransforms (applyTokenTransform tokens t) ts
> applyTokenTransforms tokens [] = tokens
> matchPair :: Char -> Char -> TokenTransform
> matchPair char1 char2 = TokenTransform pred combiner
> where isChar (TokenChar tc) ch = content tc == [ch]
> isChar _ _ = False
> pred [] = 0
> pred [x] = 0
> pred (el1 : el2 : els) | isChar el1 char1 && isChar el2 char2 = 2
> | otherwise = 0
> combiner (el1 : el2 : els) = (combineTokenChars el1 el2 : els)
> where combineTokenChars (TokenChar tc1) (TokenChar tc2) =
> let startPos = (start . position)
> endPos = (end . position)
> newTokenPosition = TokenPosition (startPos tc1) (endPos tc2)
> in TokenChar $ TokenContent newTokenPosition (content tc1 ++ content tc2)
> matchOps :: [TokenTransform]
> matchOps = [matchPair '=' '=', matchPair '>' '=', matchPair '<' '=']
Friday, August 31, 2007
Pretty printing comments
(Note: there's a new demo implementing what's described below; sadly, snapdrive.net seems to have a lot of downtime, if the link doesn't work please try later)
Pretty printing is simple, right? Just render the syntax tree as text in a pretty way and that's it.
Hmmm. What about comments? Some pretty printers (Synpl earlier versions included) just throw away comments, since they don't appear in the syntax tree. This is very wrong, as it loses precious information.
If we pretty print comments, where do we place them? The shape of the program may have changed drastically by reformatting it.
The solution I adopted is pretty naive, but it works rather well. This is what I do:
The solution? Group consecutive comments together into a larger comment, and preserve the blank lines in between.
After implementing comment grouping things work as expected. There are plenty of things left to chance, such as:
Pretty printing is simple, right? Just render the syntax tree as text in a pretty way and that's it.
Hmmm. What about comments? Some pretty printers (Synpl earlier versions included) just throw away comments, since they don't appear in the syntax tree. This is very wrong, as it loses precious information.
If we pretty print comments, where do we place them? The shape of the program may have changed drastically by reformatting it.
The solution I adopted is pretty naive, but it works rather well. This is what I do:
- when tokenizing the source, collect comments in a separate list; record the content and the starting and ending positions for each comment;
- allow some syntax tree nodes to have "after" and "before" comments; a good list of nodes to allow comments is: statements, item declarations in "DECLARE" sections of blocks, functions, procedures, triggers, packages (both spec and body) and types (also both spec and body);
- associate each comment with the closest node that accepts comments and doesn't overlap the comment;
- when pretty printing the node, print the comments with the same level of indentation as the node (for both "above" and "before" list of comments);
There are three comments here. They should all be associated to the null statement after the else. This is what happens for the first two comments. But the third is "closer" to the second null statement, and is therefore inserted in its "before" list and shows between end if and null in the pretty print. This is clearly wrong. Also, the blank line between comments is lost in translation.
begin
if 1 < a then
c := 1;
else
null;
-- uncomment next PL/SQL line to remove warnings about variable c
-- sometimes being used before initialization
--c := 2;
end if;
null;
end;
The solution? Group consecutive comments together into a larger comment, and preserve the blank lines in between.
After implementing comment grouping things work as expected. There are plenty of things left to chance, such as:
- what to do with code inside comments
- what about broken code inside comments
- what about formatting paragraphs in comments so they fit in the required number of columns?
Friday, August 17, 2007
New Synpl demo
Here's the first demo since switching languages to Scala.
What's in there:
The GUI was tested with Java 1.6.
What's in there:
- a parser for many SQL and PL/SQL language elements (blocks, basic variable definitions, most types of statements, most DML stuff, procedures, functions, packages)
- a basic analyzer (can look at a PL/SQL block and report variables that are used before being initialized, shadowed variables (same names used in inner blocks), variables that are defined and/or initialized but are never used (usage is not detected in all cases, and initialization by INTO clauses is not recognized)
- a pretty printer (adds missing elements - for instance, if the user forgot a 'THEN', the parser will report an error but recover and the pretty printer will show the source with all such errors corrected)
- a small GUI to help with testing
- many SQL and PL/SQL language elements are not identified by the parser (explicit join syntax for SQL, variable initialization at definition, PL/SQL objects and arrays etc.)
- most of the analyzer features
- advanced pretty printing (such as: detect total length of a SELECT query, and if it doesn't overflow the current line, print it on a single line)
The GUI was tested with Java 1.6.
Thursday, August 16, 2007
Synpl in Scala
I've decided to rewrite synpl in Scala. It's much better than C# for this particular task because of several reasons:
Right now a significant chunk of the PL/SQL grammar is implemented and some static analysis also works (detecting variable use before initialization). There's plenty of work to be done, but it looks like Scala is a great language choice.
Probably tomorrow or the day after I'll put together a new demo.
- it compiles to Java bytecodes, which means I can build a JAR file and then use it to build JDeveloper or SqlDeveloper extensions
- Scala has pattern matching and "case classes" which make it very easy to build AST trees and to walk those trees to do static analysis
Right now a significant chunk of the PL/SQL grammar is implemented and some static analysis also works (detecting variable use before initialization). There's plenty of work to be done, but it looks like Scala is a great language choice.
Probably tomorrow or the day after I'll put together a new demo.
Subscribe to:
Posts (Atom)