From 83e102aba11cfe20a5e71456e4090645b7066b7c Mon Sep 17 00:00:00 2001 From: Gavin Kistner Date: Tue, 8 Jul 2008 22:18:48 -0600 Subject: [PATCH] Parenthesized sub-expressions in the parser, and supported by numeric operators --- core.lua | 25 +++++-- core/number.lua | 50 ++++++++------ mab | 6 ++ parser.lua | 79 ++++++++++------------ test/acceptance/18_subexpressions.expected | 1 + test/acceptance/18_subexpressions.mab | 1 + test/test_acceptance.lua | 3 +- 7 files changed, 93 insertions(+), 72 deletions(-) create mode 100644 test/acceptance/18_subexpressions.expected create mode 100644 test/acceptance/18_subexpressions.mab diff --git a/core.lua b/core.lua index 985c54c..a968981 100644 --- a/core.lua +++ b/core.lua @@ -219,12 +219,26 @@ end -- ########################################################################## +function slurpNextValue( callState ) + local theNextValue = callState.message.next + if theNextValue ~= Roots['nil'] then + callState.callingContext.nextMessage = theNextValue.next + if runtime.isKindOf( theNextValue, Roots.Expression ) then + theNextValue = evaluateExpression( theNextValue, callState.callingContext ) + elseif runtime.isKindOf( theNextValue, Roots.Message ) then + theNextValue = sendMessage( callState.callingContext, theNextValue, callState.callingContext ) + end + end + return theNextValue +end + +-- ########################################################################## + -- TODO: remove this debug function (or more to debug utils) -function printObjectAsXML( object, depth, recursingFlag ) +function printObjectAsXML( object, showReferenced, depth, recursingFlag ) if not depth then depth = 0 end if not recursingFlag then - print( "" ) _G.objectsPrinted = {} end @@ -265,7 +279,7 @@ function printObjectAsXML( object, depth, recursingFlag ) if #objectShowingChildren > 0 then print( ">" ) for _,childObj in ipairs(objectShowingChildren) do - printObjectAsXML( childObj, depth+1, true ) + printObjectAsXML( childObj, showReferenced, depth+1, true ) end print( indent.."" ) else @@ -275,14 +289,13 @@ function printObjectAsXML( object, depth, recursingFlag ) _G.objectsPrinted[ object ] = true - if not recursingFlag then + if showReferenced and not recursingFlag then print( "" ) for id,object in ipairs(runtime.ObjectById) do if not _G.objectsPrinted[ object ] and not runtime.luastring[ object ] and not runtime.luanumber[ object ] then - printObjectAsXML( object, 0, true ) + printObjectAsXML( object, showReferenced, 0, true ) end end - print( "" ) end end diff --git a/core/number.lua b/core/number.lua index e6417f0..8884fe3 100644 --- a/core/number.lua +++ b/core/number.lua @@ -15,12 +15,11 @@ Roots.Number['+'] = createLuaFunc( 'addend', function( context ) -- Number#+ local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.addend ] if not rvalue then - local theNextMessageOrLiteral = context.callState.message.next - if theNextMessageOrLiteral == Roots['nil'] then - error( "Number#+ is missing an addend" ) + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#+ is mising an addend" ) end - context.callState.callingContext.nextMessage = theNextMessageOrLiteral.next - rvalue = runtime.luanumber[ sendMessage( context.callState.callingContext, theNextMessageOrLiteral, context.callState.callingContext ) ] + rvalue = runtime.luanumber[ rvalue ] end return runtime.number[ lvalue + rvalue ] end ) @@ -29,12 +28,11 @@ Roots.Number['-'] = createLuaFunc( 'subtrahend', function( context ) -- Number#- local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.subtrahend ] if not rvalue then - local theNextMessageOrLiteral = context.callState.message.next - if theNextMessageOrLiteral == Roots['nil'] then - error( "Number#- is missing a subtrahend" ) + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#- is mising a subtrahend" ) end - context.callState.callingContext.nextMessage = theNextMessageOrLiteral.next - rvalue = runtime.luanumber[ sendMessage( context.callState.callingContext, theNextMessageOrLiteral, context.callState.callingContext ) ] + rvalue = runtime.luanumber[ rvalue ] end return runtime.number[ lvalue - rvalue ] end ) @@ -42,12 +40,26 @@ end ) Roots.Number['>'] = createLuaFunc( 'rvalue', function( context ) -- Number#> local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.rvalue ] + if not rvalue then + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#> is mising an rvalue" ) + end + rvalue = runtime.luanumber[ rvalue ] + end return (lvalue > rvalue) and Roots['true'] or Roots['false'] end ) Roots.Number['<'] = createLuaFunc( 'rvalue', function( context ) -- Number#< local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.rvalue ] + if not rvalue then + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#< is mising an rvalue" ) + end + rvalue = runtime.luanumber[ rvalue ] + end return (lvalue < rvalue) and Roots['true'] or Roots['false'] end ) @@ -55,12 +67,11 @@ Roots.Number['*'] = createLuaFunc( 'multiplicand', function( context ) -- Number local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.multiplicand ] if not rvalue then - local theNextMessageOrLiteral = context.callState.message.next - if theNextMessageOrLiteral == Roots['nil'] then - error( "Number#* is missing a multiplicand" ) + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#* is mising a multiplicand" ) end - context.callState.callingContext.nextMessage = theNextMessageOrLiteral.next - rvalue = runtime.luanumber[ sendMessage( context.callState.callingContext, theNextMessageOrLiteral, context.callState.callingContext ) ] + rvalue = runtime.luanumber[ rvalue ] end return runtime.number[ lvalue * rvalue ] end ) @@ -69,12 +80,11 @@ Roots.Number['/'] = createLuaFunc( 'divisor', function( context ) -- Number#/ local lvalue = runtime.luanumber[ context.self ] local rvalue = runtime.luanumber[ context.divisor ] if not rvalue then - local theNextMessageOrLiteral = context.callState.message.next - if theNextMessageOrLiteral == Roots['nil'] then - error( "Number#/ is missing a divisor" ) + rvalue = slurpNextValue( context.callState ) + if rvalue == Roots['nil'] then + error( "Number#/ is mising a divisor" ) end - context.callState.callingContext.nextMessage = theNextMessageOrLiteral.next - rvalue = runtime.luanumber[ sendMessage( context.callState.callingContext, theNextMessageOrLiteral, context.callState.callingContext ) ] + rvalue = runtime.luanumber[ rvalue ] end return runtime.number[ lvalue / rvalue ] end ) diff --git a/mab b/mab index ad898c2..d76f658 100755 --- a/mab +++ b/mab @@ -71,6 +71,12 @@ if arg.sourcefile then LastCoreObjectIndex = #runtime.ObjectById core.Roots.Lawn.program = parser.parse( code ) LastParsedObjectIndex = #runtime.ObjectById + + if arg.debugLevel and arg.debugLevel >= 3 then + print( "Parsed program tree:" ) + core.printObjectAsXML( core.Roots.Lawn.program ) + end + core.evaluateChunk( core.Roots.Lawn.program ) LastRuntimeObjectIndex = #runtime.ObjectById end diff --git a/parser.lua b/parser.lua index e75b179..3a60a18 100644 --- a/parser.lua +++ b/parser.lua @@ -7,10 +7,11 @@ require 'utils' module('parser',package.seeall) grammar = [[ - Chunk <- &. -> startChunk ( )* -> endChunk - Expression <- &. -> startExpr (//)+ -> endExpr - ArgExpression <- -> addArgChunk ("," / &) - Message <- ( / ) -> startMessage ? * -> endMessage + Chunk <- &. -> startChunk ( )* -> closeChunk + Expression <- &. -> startExpr (///)+ -> closeExpr + SubExpression <- + ArgExpression <- ("," / &) + Message <- ( / ) -> startMessage ? * -> closeMessage Arguments <- * Identifier <- [a-zA-Z_]+ Operator <- [=~`!@$%^&*|<>?/\\+-]+ @@ -26,53 +27,37 @@ grammar = [[ ]] ast = {} -exprStack = {} -chunkStack = {} -argsStack = {} -messageStack = {} -stringStack = {} -parseFuncs = {} -function parseFuncs.startChunk( ) - local chunk = { tag="chunk" } - table.insert( chunkStack, chunk ) +function pushNode( tagName, str ) + table.insert( ast, { tag=tagName, str=str } ) end -function parseFuncs.endChunk( inMatch ) - table.insert( ast, table.remove( chunkStack ) ) -end - -function parseFuncs.startExpr( inMatch ) - local expression = { tag="expression" } - table.insert( exprStack, expression ) -end - -function parseFuncs.endExpr( inMatch ) - table.insert( chunkStack[#chunkStack], table.remove( exprStack ) ) -end - -function parseFuncs.startMessage( inMatch ) - local message = { tag="message", str=inMatch } - table.insert( messageStack, message ) -end - -function parseFuncs.endMessage( inMatch ) - table.insert( exprStack[#exprStack], table.remove( messageStack ) ) -end - -function parseFuncs.addArgChunk( ) - table.insert( messageStack[#messageStack], table.remove( ast ) ) +function popNode( expectedTagName ) + local node = table.remove( ast ) + if node.tag ~= expectedTagName then + error( "Popped a "..node.tag.." off the ast stack when I expected a "..expectedTagName ) + end + if #ast > 0 then + table.insert( ast[#ast], node ) + else + ast.rootNode = node + end end -function parseFuncs.addString( inMatch ) - local string = { tag="string", str=inMatch } - table.insert( exprStack[#exprStack], string ) +function addChild( tagName, str ) + table.insert( ast[#ast], { tag=tagName, str=str } ) end -function parseFuncs.addNumber( inMatch ) - local number = { tag="number", str=inMatch } - table.insert( exprStack[#exprStack], number ) -end +parseFuncs = { + startChunk = function( ) pushNode( 'chunk' ) end, + closeChunk = function( ) popNode( 'chunk' ) end, + startExpr = function( ) pushNode( 'expression' ) end, + closeExpr = function( ) popNode( 'expression' ) end, + startMessage = function( s ) pushNode( 'message', s ) end, + closeMessage = function( ) popNode( 'message' ) end, + addString = function( s ) addChild( 'string', s ) end, + addNumber = function( s ) addChild( 'number', s ) end +} function parseFile( file ) return parse( io.input(file):read("*a") ) @@ -84,7 +69,7 @@ function parse( code ) table.dump( ast ) error( "Failed to parse code! (Got to around char "..tostring(matchLength).." / "..(#code)..")" ) end - return codeFromAST( table.remove( ast ) ) + return codeFromAST( ast.rootNode ) end function codeFromAST( t ) @@ -132,6 +117,10 @@ end ) function runString( code ) core.Roots.Lawn.program = parser.parse( code ) + if arg.debugLevel and arg.debugLevel >= 3 then + print( "Parsed program tree:" ) + core.printObjectAsXML( core.Roots.Lawn.program ) + end core.evaluateChunk( core.Roots.Lawn.program ) -- TODO: error codes end \ No newline at end of file diff --git a/test/acceptance/18_subexpressions.expected b/test/acceptance/18_subexpressions.expected new file mode 100644 index 0000000..f70d7bb --- /dev/null +++ b/test/acceptance/18_subexpressions.expected @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/test/acceptance/18_subexpressions.mab b/test/acceptance/18_subexpressions.mab new file mode 100644 index 0000000..609493f --- /dev/null +++ b/test/acceptance/18_subexpressions.mab @@ -0,0 +1 @@ +p( 2 + ( 8 * 5 ) ) \ No newline at end of file diff --git a/test/test_acceptance.lua b/test/test_acceptance.lua index a58333d..b579607 100644 --- a/test/test_acceptance.lua +++ b/test/test_acceptance.lua @@ -27,7 +27,8 @@ tests = { "15_ObjectScope", "15b_SubtleObjectScope", "16_locals_shadow_context", - "17_scope_resolution" + "17_scope_resolution", + "18_subexpressions" } function setup()