Skip to content

Commit

Permalink
Merge pull request #15 from jutge-org/debugger
Browse files Browse the repository at this point in the history
Improve debugger API
  • Loading branch information
Ricard Gascons authored Dec 18, 2016
2 parents c02810b + 81c0643 commit 0c743cd
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 45 deletions.
34 changes: 34 additions & 0 deletions debugger/steps.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{ instructionQueue } = require '../interpreter/vm-state'
Ast = require '../parser/ast'
{ getFunctionMap } = require '../interpreter/function'

{ NODES, STATEMENTS, OPERATORS } = Ast

@stepOut = ->
i = instructionQueue.length - 1
while i >= 0
break if instructionQueue[i].getType() is NODES.END_FUNC_BLOCK
--i
instructionQueue[--i].stop = yes

@stepOver = ->
i = instructionQueue.length - 1
while i >= 0
if instructionQueue[i].getType() is NODES.FUNCALL
instructionQueue[i].stop = yes
break
if instructionQueue[i].getType() is NODES.END_FUNC_BLOCK and i > 0
instructionQueue[--i].stop = yes
break
--i

@stepInto = ->
instr = instructionQueue[instructionQueue.length - 1]
if instr.getType() is NODES.FUNCALL
{ instructions } = getFunctionMap()[instr.getChild(0).getChild(0)]
console.log instructions
instructions.getChild(0).stop = yes
else
instr.stop = yes

module.exports = @
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ gulp.task('dev', function() {
.transform(coffeeify)
.transform(jisonify)
.bundle()
.pipe(source('index.js'))
.pipe(source('index.min.js'))
.pipe(gulp.dest('./lib'))
});

Expand Down
20 changes: 17 additions & 3 deletions index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
{ checkSemantics } = require './semantics/'
interpreter = require './interpreter/'
Ast = require './parser/ast.coffee'
io = require './interpreter/io'
Stack = require './interpreter/stack'
{ stepInto, stepOver, stepOut } = require './debugger/steps'

parser.yy = { Ast }

Expand All @@ -18,14 +21,25 @@ parser.yy = { Ast }

loop
{ value, done } = iterator.next()
yield value
break unless not done
result = value
yield result
yield value: value, stack: Stack.stack
yield 0

@hooks = {
setInput: (input) -> io.setInput(io.STDIN, input)
isInputBufferEmpty: -> io.isInputBufferEmpty(io.STDIN)
modifyVariable: (stackNumber, varName, value) -> Stack.stack[stackNumber].variables[varName].value = value
}

@events = {
onstdout: (cb) -> interpreter.onstdout cb
}

@actions = {
stepOut: -> stepOut()
stepOver: -> stepOver()
stepInto: -> stepInto()
}


self.cmm = @
23 changes: 16 additions & 7 deletions interpreter/function.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ Stack = require './stack'
Ast = require '../parser/ast'
Error = require '../error'
Expression = require './expression'
{ instructionQueue } = require './vm-state'


{ NODES, TYPES } = Ast

module.exports = @

# Of the form: { <funcId>: { type: tipus, argIds: [id1, id2, ..., idn] } }
# Of the form: { <funcId>: { type: tipus, argTypes: [ty1, ty2, ..., tyn] argIds: [id1, id2, ..., idn], instructions: instruccions } }
functions = null

@getFunctionMap = -> functions

@mapFunctions = (T) ->
functions = {}
assert.strictEqual T.getType(), NODES.BLOCK_FUNCTIONS
for functionTree in T.getChildren()
assert.strictEqual functionTree.getType(), TYPES.FUNCTION
funcId = functionTree.getChild(1).getChild(0)
argTypes = (argAst.getChild(0) for argAst in functionTree.getChild(2).getChildren())
argIds = (argAst.getChild(1).getChild(0) for argAst in functionTree.getChild(2).getChildren())
type = functionTree.getChild(0)
functions[funcId] = { type, argIds, instructions: functionTree.getChild(3) }
functions[funcId] = { type, argTypes, argIds, instructions: functionTree.getChild(3) }
return

@initFunction = (T) ->
Expand All @@ -34,23 +38,28 @@ functions = null

assert func?, 'Function ' + funcId + ' not declared'

{ type, argIds, instructions } = func
{ type, argTypes, argIds, instructions } = func

assert argIds.length is argValuesAst.getChildCount()

argIdValuePairs =
for id, i in argIds
type: argTypes[i]
id: id
value: Expression.evaluateExpression(argValuesAst.getChild(i))

result = null

Stack.pushActivationRecord()
Stack.pushActivationRecord(funcId, argIds)

for { id, value } in argIdValuePairs
Stack.defineVariable id, value
for { type, id, value } in argIdValuePairs
Stack.defineVariable id, type, value, param=yes

instructions
instructionQueue.push new Ast NODES.END_FUNC_BLOCK, []
instructionQueue.push instructions

@finalizeFunction = ->
loop
instr = instructionQueue.pop()
break if instr.getType() is NODES.END_FUNC_BLOCK
Stack.popActivationRecord()
24 changes: 9 additions & 15 deletions interpreter/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ assert = require 'assert'
Error = require '../error'
Ast = require '../parser/ast'
Stack = require './stack'
{ mapFunctions, initFunction, finalizeFunction } = require './function'
{ mapFunctions, initFunction } = require './function'
{ initRunner, executeInstruction } = require './runner'
io = require './io'

Expand All @@ -19,20 +19,14 @@ module.exports = @
io.reset()
io.setInput(io.STDIN, input)

try
instructions = initFunction new Ast(NODES.FUNCALL, [new Ast(NODES.ID, ["main"]), new Ast(NODES.PARAM_LIST, [])])
initRunner instructions
iterator = executeInstruction()
loop
{ value, done } = iterator.next()
yield value
break unless not done
status = value
finalizeFunction()
catch error
console.error error.stack ? error.message ? error
io.output io.STDERR, error.message
status = error.code
initFunction new Ast(NODES.FUNCALL, [new Ast(NODES.ID, ["main"]), new Ast(NODES.PARAM_LIST, [])])
initRunner()
iterator = executeInstruction()
loop
{ value, done } = iterator.next()
break unless not done
yield value
status = value

yield { status, stderr: io.getStream(io.STDERR) }

Expand Down
3 changes: 3 additions & 0 deletions interpreter/io.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = class IO

IO.streams[stream] += string
IO.stdoutCB IO.streams[IO.STDOUT]
IO.streams[IO.STDOUT] = '';

@setInput: (stream, input) ->
assert (typeof input is "string")
Expand All @@ -48,3 +49,5 @@ module.exports = class IO
@getStream: (stream) -> IO.streams[stream]

@setStdoutCB: (cb) -> IO.stdoutCB = cb

@isInputBufferEmpty: (stream) -> IO.streams[stream].length is 0
13 changes: 7 additions & 6 deletions interpreter/runner.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ io = require './io'

module.exports = @

@initRunner = (T) ->
@root = T
instructionQueue = [T]
@initRunner = ->
@openScopes = {}

@getNumberInstructions = ->
Expand All @@ -35,14 +33,15 @@ executeInstructionHelper = (T) ->
unwrapBlock T
when NODES.DECLARATION
declarations = T.getChild 1
type = T.getChild 0
for declaration in declarations
if declaration.getType() is OPERATORS.ASSIGN
varName = declaration.child().child()
value = evaluateExpression declaration.getChild 1
Stack.defineVariable varName, value
Stack.defineVariable varName, type, value
else if declaration.getType() is NODES.ID
varName = declaration.child()
Stack.defineVariable varName
Stack.defineVariable varName, type
when STATEMENTS.COUT
for outputItem in T.getChildren()
io.output io.STDOUT, evaluateExpression(outputItem)
Expand Down Expand Up @@ -87,7 +86,7 @@ executeInstructionHelper = (T) ->
when NODES.CLOSE_SCOPE
Stack.closeScope()
when NODES.FUNCALL
pushInstruction initFunction T
initFunction T
when STATEMENTS.CIN
allRead = yes
for inputItem in T.getChildren()
Expand All @@ -107,6 +106,8 @@ executeInstructionHelper = (T) ->
Stack.setVariable id, null
allRead = no
cinStack.push allRead
when NODES.END_FUNC_BLOCK
(->)()
else
evaluateExpression T

Expand Down
16 changes: 8 additions & 8 deletions interpreter/stack.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ module.exports = class Stack
@stack: []
@currentAR: null

@pushActivationRecord: ->
@currentAR = { scopesStack: [], variables: {} }
@pushActivationRecord: (funcName, args) ->
@currentAR = { scopesStack: [], variables: {}, funcName: funcName, args: args }
@stack.push @currentAR

@popActivationRecord: ->
Expand All @@ -17,28 +17,28 @@ module.exports = class Stack
if @stack.length > 0 then @stack[@stack.length - 1] else null

# Parameter value is optional, if ommited means variable has been declared but not yet assigned
@defineVariable: (name, value = null) ->
@defineVariable: (name, type, value = null) ->
assert @currentAR?
assert (typeof name is "string")

@currentAR.variables[name] = value
@currentAR.variables[name] = { type: type, value: value }

@getVariable: (name) ->
assert @currentAR?
assert (typeof name is "string")
assert typeof @currentAR.variables[name] isnt "undefined"
assert typeof @currentAR.variables[name].value isnt "undefined"

if @currentAR.variables[name] is null
throw Error.GET_VARIABLE_NOT_ASSIGNED.complete('name', name)
else
@currentAR.variables[name]
@currentAR.variables[name].value

@setVariable: (name, value) ->
assert @currentAR?
assert (typeof name is "string")
assert typeof @currentAR.variables[name] isnt "undefined"
assert typeof @currentAR.variables[name].value isnt "undefined"

@currentAR.variables[name] = value
@currentAR.variables[name].value = value

@openNewScope: ->
assert @currentAR?
Expand Down
10 changes: 8 additions & 2 deletions parser/ast.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ module.exports = class Ast
CLOSE_SCOPE: 'CLOSE_SCOPE'
FUNC_VALUE: 'FUNC_VALUE'
CIN_VALUE: 'CIN_VALUE'
END_FUNC_BLOCK: 'END_FUNC_BLOCK'
})

@CASTS: Object.freeze({
Expand Down Expand Up @@ -101,6 +102,7 @@ module.exports = class Ast

constructor: (@type, @children, @leaf = no) ->
@instr = no
@instrNumber = -1
assert (typeof @type is "string")
assert Array.isArray(@children)

Expand Down Expand Up @@ -136,10 +138,14 @@ module.exports = class Ast

setIsInstr: (@instr) ->

setInstrNumber: (@instrNumber) ->

setId: (@id) ->

addChild: (child, instr=no) ->
child.setIsInstr yes if instr
addChild: (child, instr=no, instrNumber=-1) ->
if instr
child.setIsInstr yes
child.setInstrNumber instrNumber
@children.push child

setChild: (i, value) -> @children[i] = value
Expand Down
6 changes: 3 additions & 3 deletions parser/grammar.jison
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ arg

block_instr
: block_instr instruction
{$$.addChild($2, instr=true);}
{$$.addChild($2, instr=true, instrNumber=@2.first_line);}
|
{$$ = new yy.Ast('BLOCK-INSTRUCTIONS', []);}
;
Expand Down Expand Up @@ -206,7 +206,7 @@ if
: IF '(' expr ')' instruction_body %prec THEN
{$$ = new yy.Ast('IF-THEN', [$3, $5]);}
| IF '(' expr ')' instruction_body else
{$$ = new yy.Ast('IF-THEN-ELSE', [$3, $5, $6]);}
{$6.setIsInstr(true); $6.setInstrNumber(@6.first_line); $$ = new yy.Ast('IF-THEN-ELSE', [$3, $5, $6]);}
;

while
Expand Down Expand Up @@ -256,7 +256,7 @@ block_cout

instruction_body
: instruction
{$1.setIsInstr(true); $$ = new yy.Ast('BLOCK-INSTRUCTIONS', [$1]);}
{$1.setIsInstr(true); $1.setInstrNumber(@1.first_line); $$ = new yy.Ast('BLOCK-INSTRUCTIONS', [$1]);}
| '{' block_instr '}'
{$$ = $2;}
;
Expand Down
2 changes: 2 additions & 0 deletions semantics/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ checkAndPreprocess = (ast, definedVariables, functionId) ->

return TYPES.VOID

# TODO: replace by jison enumerations
enumerateInstructions = (T) ->
for child in T.getChildren()
child.setId ++NODE_INDEX if child.instr
Expand Down Expand Up @@ -504,6 +505,7 @@ preprocessFunctionAndCinNodes = (T, currentBlockInstr, currentInstr) ->
enumerateInstructions blockInstructionsAst
preprocessFunctionAndCinNodes blockInstructionsAst, blockInstructionsAst, blockInstructionsAst.getChild(0)
blockInstructionsAst.getChildren().push new Ast(STATEMENTS.RETURN, []) if returnType is TYPES.VOID
blockInstructionsAst.getChildren().push new Ast(STATEMENTS.RETURN, []) if functionId is "main"

if definedVariables.main isnt TYPES.FUNCTION
throw Error.MAIN_NOT_DEFINED
Expand Down

0 comments on commit 0c743cd

Please sign in to comment.