changeset 7:2db4d2b362e6

Added xml project
author windel
date Sat, 15 Oct 2011 10:03:21 +0200
parents 1784af239df4
children edd70006d3e4
files ide/compiler/compiler.py ide/compiler/parser.py ide/ide/ide.py ide/project.py ide/runtests.py os/os.lcp
diffstat 6 files changed, 175 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/ide/compiler/compiler.py	Fri Oct 07 11:20:06 2011 +0200
+++ b/ide/compiler/compiler.py	Sat Oct 15 10:03:21 2011 +0200
@@ -4,6 +4,7 @@
 from .parser import Parser
 from .codegenerator import CodeGenerator
 from .nodes import ExportedSymbol
+from .errors import CompilerException
 
 class Compiler:
    versie = '0.9.3'
@@ -20,7 +21,10 @@
       # Pass 1: parsing and type checking
       tokens = lexer.tokenize(src) # Lexical stage
       p = Parser(tokens)
-      ast = p.parseModule() # Parse a module
+      try:
+         ast = p.parseModule() # Parse a module
+      except CompilerException as e:
+         p.errorlist.append( (e.row, e.col, e.msg) )
       if len(p.errorlist) > 0:
          self.errorlist = p.errorlist
          return
--- a/ide/compiler/parser.py	Fri Oct 07 11:20:06 2011 +0200
+++ b/ide/compiler/parser.py	Sat Oct 15 10:03:21 2011 +0200
@@ -27,7 +27,7 @@
        self.NextToken()
        return v
      else:
-       self.Error('Excected: "{0}", got "{1}"'.format(typ, self.token))
+       self.Error('Excected: "{0}", got "{1}"'.format(typ, self.token.val))
 
    def hasConsumed(self, typ):
       if self.token.typ == typ:
@@ -602,6 +602,9 @@
      except CompilerException as e:
          print(e)
          self.errorlist.append( (e.row, e.col, e.msg))
+         # Do error recovery by skipping all tokens until next ; or end
+         while not (self.token.typ == ';' or self.token.typ == 'end'):
+            self.Consume(self.token.typ)
          return EmptyStatement()
 
    def parseStatementSequence(self):
--- a/ide/ide/ide.py	Fri Oct 07 11:20:06 2011 +0200
+++ b/ide/ide/ide.py	Sat Oct 15 10:03:21 2011 +0200
@@ -4,6 +4,7 @@
 from .codeeditor import CodeEdit
 from .astviewer import AstViewer
 import base64
+from project import Project
 
 source = """
 module x;
@@ -47,14 +48,13 @@
    def __init__(self, parent=None):
       super(BuildErrors, self).__init__(parent)
       model = QStandardItemModel()
-      item = QStandardItem('Hallo dan!')
-      model.appendRow(item)
       self.setModel(model)
       self.clicked.connect(self.itemSelected)
    def setErrorList(self, errorlist):
       model = QStandardItemModel()
       for e in errorlist:
-         item = QStandardItem(str(e))
+         row, col, msg = e
+         item = QStandardItem(str(msg))
          item.setData(e)
          model.appendRow(item)
       self.setModel(model)
@@ -66,6 +66,46 @@
       err = item.data()
       self.sigErrorSelected.emit(err)
 
+class ProjectView(QWidget):
+   def __init__(self, parent=None):
+      super(ProjectView, self).__init__(parent)
+      self.treeview = QTreeView(self)
+      l = QVBoxLayout(self)
+      l.addWidget(self.treeview)
+      pm = QPixmap()
+      pm.loadFromData(lcfospng)
+      self.projectIcon = QIcon(pm)
+   def setProject(self, project):
+      self.project = project
+      model = QStandardItemModel()
+      root = model.invisibleRootItem()
+      pitem = QStandardItem(self.projectIcon, project.name)
+      root.appendRow(pitem)
+      for f in self.project.files:
+         fitem = QStandardItem(f)
+         pitem.appendRow(fitem)
+      self.treeview.setModel(model)
+      self.treeview.expandAll()
+
+class AboutDialog(QDialog):
+   def __init__(self, parent=None):
+      super(AboutDialog, self).__init__(parent)
+      self.setWindowTitle('About')
+      l = QVBoxLayout(self)
+      txt = QTextEdit(self)
+      txt.setReadOnly(True)
+      aboutText = """<h1>lcfOS IDE</h1>
+      <p>An all-in-one IDE for OS development.</p>
+      <p>https://www.assembla.com/spaces/lcfOS/wiki</p>
+      <p>Author: Windel Bouwman</p>
+      """
+      txt.append(aboutText)
+      l.addWidget(txt)
+      but = QPushButton('OK')
+      but.setDefault(True)
+      but.clicked.connect(self.close)
+      l.addWidget(but)
+
 class Ide(QMainWindow):
   def __init__(self, parent=None):
     super(Ide, self).__init__(parent)
@@ -77,6 +117,7 @@
     # Create menus:
     self.viewMenu = self.menuBar().addMenu('View')
     self.projectMenu = self.menuBar().addMenu('Project')
+    self.helpMenu = self.menuBar().addMenu('Help')
 
     # Create components:
     self.codeedit = CodeEdit()
@@ -93,11 +134,26 @@
     self.addComponent('Build errors', self.builderrors)
     self.builderrors.sigErrorSelected.connect(self.errorSelected)
 
+    self.projectview = ProjectView()
+    self.addComponent('Project', self.projectview)
+
+    # About dialog:
+    self.aboutDialog = AboutDialog()
+    self.aboutDialog.setWindowIcon(QIcon(icon))
     # Create actions:
     self.buildAction = QAction('Build!', self)
     self.buildAction.setShortcut(QKeySequence('F7'))
     self.projectMenu.addAction(self.buildAction)
     self.buildAction.triggered.connect(self.buildFile)
+    self.openProjectAction = QAction("Open project", self)
+    self.openProjectAction.triggered.connect(self.openProject)
+    self.projectMenu.addAction(self.openProjectAction)
+    self.helpAction  = QAction('Help', self)
+    self.helpAction.setShortcut(QKeySequence('F1'))
+    self.helpMenu.addAction(self.helpAction)
+    self.aboutAction = QAction('About', self)
+    self.helpMenu.addAction(self.aboutAction)
+    self.aboutAction.triggered.connect(self.aboutDialog.open)
 
     # END of UI construction
 
@@ -120,9 +176,18 @@
         self.restoreState(self.settings.value('mainwindowstate'))
      if self.settings.contains('mainwindowgeometry'):
         self.restoreGeometry(self.settings.value('mainwindowgeometry'))
+     if self.settings.contains('openedproject'):
+        self.projectfile = self.settings.value('openedproject')
+        print(self.projectfile, type(self.projectfile))
+        self.loadProject()
+     else:
+        self.projectfile = ""
+
+  
   def closeEvent(self, ev):
      self.settings.setValue('mainwindowstate', self.saveState())
      self.settings.setValue('mainwindowgeometry', self.saveGeometry())
+     self.settings.setValue('openedproject', self.projectfile)
      self.codeedit.saveFile()
      ev.accept()
 
@@ -137,18 +202,27 @@
      row, col, msg = err
      self.codeedit.highlightErrorLocation(row, col)
 
+  def loadProject(self):
+     if self.projectfile:
+        self.project = Project()
+        self.project.load(self.projectfile)
+        self.projectview.setProject(self.project)
+
+  def openProject(self):
+     self.projectfile = QFileDialog.getOpenFileName(self, "Choose project file", "", "lcfos Project files (*.lcp)")
+     self.loadProject()
+
   def buildFile(self):
      self.buildOutput.clear()
      self.codeedit.clearErrors()
      source = str(self.codeedit.toPlainText())
      self.buildOutput.append(str(self.compiler))
-     self.astViewer.setAst(None)
-     try:
-        output = self.compiler.compilesource(source)
-        self.builderrors.setErrorList(self.compiler.errorlist)
-        self.astViewer.setAst(output)
+     output = self.compiler.compilesource(source)
+     self.builderrors.setErrorList(self.compiler.errorlist)
+     self.astViewer.setAst(output)
+     if self.compiler.errorlist:
+        self.buildOutput.append("error occurred")
+     else:
         self.buildOutput.append("Done!")
-     except compiler.errors.CompilerException as e:
-        self.buildOutput.append(e.msg)
-        self.codeedit.highlightErrorLocation( e.row, e.col )
 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ide/project.py	Sat Oct 15 10:03:21 2011 +0200
@@ -0,0 +1,60 @@
+"""
+ Project that can be stored to and loaded from XML.
+"""
+
+from xml.sax import ContentHandler, make_parser
+import xml.dom.minidom as md
+import os.path
+
+class ProjectContentHandler(ContentHandler):
+   def __init__(self, project):
+      self.project =  project
+      self.inFiles = False
+   def startElement(self, name, attrs):
+      if name == 'Project':
+         self.project.name = attrs['name']
+      if name == 'Files':
+         self.inFiles = True
+      if name == 'File' and self.inFiles:
+         self.project.files.append(attrs['Filename'])
+   def endElement(self, name):
+      if name == 'Files':
+         self.inFiles = False
+
+class Project:
+   def __init__(self):
+      self.name = ""
+      self.files = []
+      self.settings = {}
+
+   def save(self, filename):
+      """ Save the project in XML format """
+      # Create document:
+      doc = md.Document()
+      # Add project:
+      project = doc.createElement("Project")
+      project.setAttribute("name", self.name)
+      doc.appendChild(project)
+
+      # Add project files:
+      filesNode = doc.createElement("Files")
+      project.appendChild(filesNode)
+      for f in self.files:
+         fe = doc.createElement("File")
+         fe.setAttribute("Filename", f)
+         filesNode.appendChild(fe)
+
+      # Write the XML file:
+      xml = doc.toprettyxml()
+      with open(filename, 'w') as f:
+         f.write(xml)
+
+   def load(self, filename):
+      """ Load the project from the XML file """
+      if not os.path.exists(filename):
+         return
+      parser = make_parser()
+      handler = ProjectContentHandler(self)
+      parser.setContentHandler(handler)
+      parser.parse(filename)
+
--- a/ide/runtests.py	Fri Oct 07 11:20:06 2011 +0200
+++ b/ide/runtests.py	Sat Oct 15 10:03:21 2011 +0200
@@ -6,6 +6,7 @@
 from compiler.parser import Parser
 from compiler import assembler
 from compiler.codegenerator import CodeGenerator
+from project import Project
 
 class CompilerTestCase(unittest.TestCase):
    """ test methods start with 'test*' """
@@ -251,6 +252,19 @@
       # nasm generates this machine code: 0x4d, 0x6b, 0xff, 0xee
       # This also works: 4D0FAFFE (another variant?? )
       assert(assembler.imulreg64('r15', 'r14') == [0x4d, 0x0f, 0xaf, 0xfe])
+   def testProject(self):
+      p = Project()
+      p.name = "Test project"
+      p.files.append('main.mod')
+      p.files.append('test.mod')
+      p.save('test.xml')
+
+      q = Project()
+      q.load('test.xml')
+
+      assert(p.name == q.name)
+      assert(p.files == q.files)
+      # TODO: remove test.xml test file
 
 if __name__ == '__main__':
    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/os/os.lcp	Sat Oct 15 10:03:21 2011 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" ?>
+<Project name="Test project">
+	<Files>
+		<File Filename="main.mod"/>
+		<File Filename="test.mod"/>
+	</Files>
+</Project>