# HG changeset patch # User windel # Date 1318665801 -7200 # Node ID 2db4d2b362e64c3119fc9a96b7ff85e9f880121b # Parent 1784af239df48b696215b367986256038c53c454 Added xml project diff -r 1784af239df4 -r 2db4d2b362e6 ide/compiler/compiler.py --- 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 diff -r 1784af239df4 -r 2db4d2b362e6 ide/compiler/parser.py --- 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): diff -r 1784af239df4 -r 2db4d2b362e6 ide/ide/ide.py --- 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 = """

lcfOS IDE

+

An all-in-one IDE for OS development.

+

https://www.assembla.com/spaces/lcfOS/wiki

+

Author: Windel Bouwman

+ """ + 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 ) + diff -r 1784af239df4 -r 2db4d2b362e6 ide/project.py --- /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) + diff -r 1784af239df4 -r 2db4d2b362e6 ide/runtests.py --- 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() diff -r 1784af239df4 -r 2db4d2b362e6 os/os.lcp --- /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 @@ + + + + + + +