From aa2ea213bf3041bedea50e375fabd4ecb9d94231 Mon Sep 17 00:00:00 2001 From: Cornelius Riemenschneider Date: Tue, 6 Apr 2010 20:05:45 +0000 Subject: [PATCH] Add the MainFunction and the Lower_Control_Flow pass. MainFunction moves all global code into one function, __MAIN__. Lower_Control_Flow lowers ifs and whiles to branch/label instructions. Foreach is currently unsupported. --- compiler/analysis/CMakeLists.txt | 2 + compiler/analysis/passes/DumpAST.cpp | 9 + .../analysis/passes/Early_Lower_Loops.cpp | 3 + .../analysis/passes/Lower_Control_Flow.cpp | 162 ++++++++++++++++++ compiler/analysis/passes/MainFunction.cpp | 52 ++++++ frontend/cli/rphp-analyzer.cpp | 14 +- include/rphp/analysis/astNodes.def | 6 + include/rphp/analysis/pAST.h | 141 +++++++++++---- include/rphp/analysis/passes/DumpAST.h | 2 + .../rphp/analysis/passes/Lower_Control_Flow.h | 62 +++++++ include/rphp/analysis/passes/MainFunction.h | 45 +++++ include/rphp/runtime/pSupport.h | 2 + runtime/pSupport.cpp | 23 +++ 13 files changed, 488 insertions(+), 35 deletions(-) create mode 100644 compiler/analysis/passes/Lower_Control_Flow.cpp create mode 100644 compiler/analysis/passes/MainFunction.cpp create mode 100644 include/rphp/analysis/passes/Lower_Control_Flow.h create mode 100644 include/rphp/analysis/passes/MainFunction.h diff --git a/compiler/analysis/CMakeLists.txt b/compiler/analysis/CMakeLists.txt index 0793aa9..c746b93 100644 --- a/compiler/analysis/CMakeLists.txt +++ b/compiler/analysis/CMakeLists.txt @@ -90,6 +90,8 @@ set(PARSER_SRC_FILES passes/Early_Lower_Loops.cpp passes/Lower_Binary_Op.cpp passes/Lower_Conditional_Expr.cpp + passes/Lower_Control_Flow.cpp + passes/MainFunction.cpp ) # PASSES diff --git a/compiler/analysis/passes/DumpAST.cpp b/compiler/analysis/passes/DumpAST.cpp index d06f444..2f3a340 100644 --- a/compiler/analysis/passes/DumpAST.cpp +++ b/compiler/analysis/passes/DumpAST.cpp @@ -426,5 +426,14 @@ bool DumpAST::visit_children_formalParam(formalParam* n) { return true; } +void DumpAST::visit_pre_label(label* n) { + currentElement_->SetAttribute("id", n->labelNo()); +} + +void DumpAST::visit_pre_branch(branch* n) { + currentElement_->SetAttribute("trueLabel", n->trueLabel()); + currentElement_->SetAttribute("falseLabel", n->falseLabel()); +} + } } } // namespace diff --git a/compiler/analysis/passes/Early_Lower_Loops.cpp b/compiler/analysis/passes/Early_Lower_Loops.cpp index 89ca1ff..58f69d0 100644 --- a/compiler/analysis/passes/Early_Lower_Loops.cpp +++ b/compiler/analysis/passes/Early_Lower_Loops.cpp @@ -46,6 +46,9 @@ namespace rphp { namespace AST { namespace Pass { * } */ stmt* Early_Lower_Loops::transform_post_whileStmt(whileStmt* n) { + if(literalBool* cond = dyn_cast(n->condition())) + if(cond->getBoolVal() == true) + return n; // break breakStmt* breakLoop = new (C_) breakStmt(NULL); diff --git a/compiler/analysis/passes/Lower_Control_Flow.cpp b/compiler/analysis/passes/Lower_Control_Flow.cpp new file mode 100644 index 0000000..80397cf --- /dev/null +++ b/compiler/analysis/passes/Lower_Control_Flow.cpp @@ -0,0 +1,162 @@ +/* ***** BEGIN LICENSE BLOCK ***** +;; Roadsend PHP Compiler +;; +;; Copyright (c) 2010 Cornelius Riemenschneider +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + ***** END LICENSE BLOCK ***** + */ + +#include "rphp/analysis/passes/Lower_Control_Flow.h" +#include "rphp/analysis/pSourceModule.h" + +namespace rphp { namespace AST { namespace Pass { + +/** + * TODO: foreach lowering, dynamic break/continues. + */ + +/** + * Foreach lowering is not implemented yet, thus reject foreachs. + */ +stmt* Lower_Control_Flow::transform_pre_forEach(forEach* n) { + assert(0 && "Foreach lowering is not implemented yet, sorry."); + return n; +} + +/* Transform + * if ($x) { y (); } + * else { z (); } + * into + * if ($x) goto L1; + * else goto L2; + * L1: + * y (); + * goto L3; + * L2: + * z (); + * goto L3; + * L3: + * + * We only need two labels, since the first edge can be an implict + * fall-through edge if we negate the condition, but this is more + * readable and understandable, as it keeps the structure of the original + * if-else statement. */ +stmt* Lower_Control_Flow::transform_post_ifStmt(ifStmt* n) { + label* L1 = currentFunction_->getNewLabel(C_); + label* L2 = currentFunction_->getNewLabel(C_); + label* L3 = currentFunction_->getNewLabel(C_); + + branch* br = new (C_) branch(n->condition()->retain(), L1, L2); + statementList stmts; + stmts.push_back(br); + stmts.push_back(L1); + if(n->trueBlock()) + stmts.push_back(n->trueBlock()->retain()); + stmts.push_back(new (C_) branch(L3)); + stmts.push_back(L2); + if(n->falseBlock()) + stmts.push_back(n->falseBlock()->retain()); + stmts.push_back(new (C_) branch(L3)); + stmts.push_back(L3); + + return new (C_) block(C_, &stmts); +} + +/** + * Remove Lbreak and Lcontinue from the respective stacks. + */ +stmt* Lower_Control_Flow::transform_post_whileStmt(whileStmt* n) { + breakLabels_.pop_back(); + continueLabels_.pop_back(); + return n; +} + +/* Transform + * while ($x) { y (); } + * into + * Lcontinue: + * y (); + * goto Lcontinue + * Lbreak: + * + * Lcontinue is the label used if you use continue in the loop, Lbreak is the label used if you use break in the loop. + */ +stmt* Lower_Control_Flow::transform_pre_whileStmt(whileStmt* n) { + assert(cast(n->condition())->getBoolVal() == true && "non-lowered while loop found!"); + + statementList stmts; + + // continue label + label* Lcontinue = currentFunction_->getNewLabel(C_); + continueLabels_.push_back(Lcontinue); + + stmts.push_back(Lcontinue); + stmts.push_back(n->body()->retain()); + stmts.push_back(new (C_) branch(Lcontinue)); + + label* Lbreak = currentFunction_->getNewLabel(C_); + breakLabels_.push_back(Lbreak); + stmts.push_back(Lbreak); + + return new (C_) block(C_, &stmts); +} + +/** + * This function lowers an exit (continue/break) from a loop and generates a branch to the appropriate label. + * Currently dynamic continue/breaks aren't supported. + */ +stmt* Lower_Control_Flow::lower_exit(std::vector& labels, expr* depthExpr) { + pInt depth = -1; + if(depthExpr == NULL) + depth = 0; + else { + //TODO: what's about literalStrings? normally they evaluate to 0, but to what evaluates '1'? + assert(!isa(depthExpr)); + if(literalInt* depthInt = dyn_cast(depthExpr)) { + // This possibly throws an exception if int>2^31-1, but that's okay, we don't support breaking on that values anyways :D + depth = depthInt->getInt(); + //TODO: error/warning on depth < 1 + // negative/zero break is always 1 + if(depth < 1) + depth = 1; + } + //TODO: Dynamic depth + } + if(depth == 0) + depth = 1; + if(depth != -1) + //TODO: replace by a better error! + assert(depth <= (pInt)labels.size() && "you're trying to break too much!"); + assert(depth != -1 && "dynamic depth currently not supported!"); + return new (C_) branch(labels[labels.size() - depth]); +} + +stmt* Lower_Control_Flow::transform_post_breakStmt(breakStmt* n) { + return lower_exit(breakLabels_, n->rVal()); +} + +stmt* Lower_Control_Flow::transform_post_continueStmt(continueStmt* n) { + return lower_exit(continueLabels_, n->rVal()); +} + +// This is needed to get the current function. +stmt* Lower_Control_Flow::transform_pre_functionDecl(functionDecl* n) { + currentFunction_ = n; + return n; +} + + +} } } // namespace diff --git a/compiler/analysis/passes/MainFunction.cpp b/compiler/analysis/passes/MainFunction.cpp new file mode 100644 index 0000000..fc165ea --- /dev/null +++ b/compiler/analysis/passes/MainFunction.cpp @@ -0,0 +1,52 @@ +/* ***** BEGIN LICENSE BLOCK ***** +;; Roadsend PHP Compiler +;; +;; Copyright (c) 2010 Cornelius Riemenschneider +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + ***** END LICENSE BLOCK ***** + */ + +#include "rphp/analysis/passes/MainFunction.h" +#include "rphp/analysis/pSourceModule.h" + +namespace rphp { namespace AST { namespace Pass { + +/** + * Moves all global code except of function and class declarations into a __MAIN__ function. + * __MAIN__ will later hold the global variables as well. + */ +stmt* MainFunction::transform_pre_block(block *n) { + // Just run on the first block. + if(firstBlock) + return n; + firstBlock = true; + statementList mainFunction; + statementList globalScope; + foreach(stmt* child, n->children()) { + if(isa(child) || isa(child)) + globalScope.push_back(child->retain()); + else + mainFunction.push_back(child->retain()); + } + + block* mainFunctionBody = new (C_) block(C_, &mainFunction); + signature* mainFunctionSig = new (C_) signature("__MAIN__", C_, NULL); + functionDecl* mainFunctionDecl = new (C_) functionDecl(mainFunctionSig, mainFunctionBody); + globalScope.push_back(mainFunctionDecl); + return new (C_) block(C_, &globalScope); +} + +} } } // namespace diff --git a/frontend/cli/rphp-analyzer.cpp b/frontend/cli/rphp-analyzer.cpp index 155aa9d..971ad98 100644 --- a/frontend/cli/rphp-analyzer.cpp +++ b/frontend/cli/rphp-analyzer.cpp @@ -43,6 +43,8 @@ #include "rphp/analysis/passes/Early_Lower_Loops.h" #include "rphp/analysis/passes/Lower_Binary_Op.h" #include "rphp/analysis/passes/Lower_Conditional_Expr.h" +#include "rphp/analysis/passes/Lower_Control_Flow.h" +#include "rphp/analysis/passes/MainFunction.h" using namespace llvm; @@ -104,11 +106,13 @@ int main( int argc, char* argv[] ) else if(runPasses) { // make all variables etc in strings accessible to passes which need them passManager.addPass(); - passManager.addPass(); // TODO: get variable names here or use variable names which the zend engine // doesn't allow for internal variables. + //TODO: take care of conditional function definitions. + passManager.addPass(); + // uses boolean && and || passManager.addPass(); // uses boolean ! @@ -121,6 +125,8 @@ int main( int argc, char* argv[] ) // do this one late in case other passes are creating return's for whatever reason... passManager.addPass(); + passManager.addPass(); + // debug output passManager.addPass(); passManager.addPass(); @@ -154,6 +160,12 @@ int main( int argc, char* argv[] ) else if (*i == "desugar") { passManager.addPass(); } + else if (*i == "lower-control-flow") { + passManager.addPass(); + } + else if (*i == "add-main-function") { + passManager.addPass(); + } } } else { diff --git a/include/rphp/analysis/astNodes.def b/include/rphp/analysis/astNodes.def index 56cd275..46c0f5a 100644 --- a/include/rphp/analysis/astNodes.def +++ b/include/rphp/analysis/astNodes.def @@ -52,6 +52,12 @@ STMT(propertyDecl, stmt) STMT(functionDecl, stmt) // LAST DECL +//FIRST MIR NODE +STMT(label, stmt) +STMT(branch, stmt) +//LAST MIR NODE + + // expr (see expr::first and last kind constants) // FIRST EXPR EXPR(exprReduce, expr) diff --git a/include/rphp/analysis/pAST.h b/include/rphp/analysis/pAST.h index 736708a..18c898b 100644 --- a/include/rphp/analysis/pAST.h +++ b/include/rphp/analysis/pAST.h @@ -529,40 +529,6 @@ class signature: public decl { IMPLEMENT_SUPPORT_MEMBERS(signature); }; -// function declaration -class functionDecl: public decl { - - enum { SIG, BODY, END_EXPR }; - stmt* children_[END_EXPR]; - -protected: - functionDecl(const functionDecl& other, pParseContext& C): decl(other) - { - memset(children_, 0, sizeof(children_)); - if(other.children_[SIG]) - children_[SIG] = other.children_[SIG]->clone(C); - if(other.children_[BODY]) - children_[BODY] = other.children_[BODY]->clone(C); - } - -public: - functionDecl(signature* sig, block* body): - decl(functionDeclKind), - children_() - { - children_[SIG] = sig; - children_[BODY] = body; - } - - signature* sig(void) { return static_cast(children_[SIG]); } - block* body(void) { return static_cast(children_[BODY]); } - - stmt::child_iterator child_begin() { return (stmt**)&children_[0]; } - stmt::child_iterator child_end() { return (stmt**)&children_[0]+END_EXPR; } - - IMPLEMENT_SUPPORT_MEMBERS(functionDecl); - -}; struct memberFlags { // these are taken by address during parse @@ -2292,6 +2258,113 @@ class exprReduce: public expr { }; +// MIR nodes + +// A label is function-local, and a function-unique label id has to be obtained from the +// functionDecl AST node. +class label: public stmt { + + pInt labelNo_; + +protected: + label(const label& other, pParseContext& C): stmt(other), labelNo_(other.labelNo_) {} + +public: + label(pUInt labelNo): stmt(labelKind), labelNo_(labelNo) {} + pUInt labelNo() const { return labelNo_; } + void setLabelNo(pUInt n) { labelNo_ = n; } + + stmt::child_iterator child_begin() { return child_iterator(); } + stmt::child_iterator child_end() { return child_iterator(); } + + IMPLEMENT_SUPPORT_MEMBERS(label); +}; + +// A branch with no condition is an unconditional branch to trueLabel. +// falseLabel is then -1 +class branch: public stmt { + + pInt trueLabel_; + pInt falseLabel_; + expr* condition_; + +protected: + branch(const branch& other, pParseContext& C): stmt(other), + trueLabel_(other.trueLabel_), falseLabel_(other.falseLabel_), condition_(0) + { + if(other.condition_) + condition_ = other.condition_->clone(C); + } + +public: + branch(expr* condition, pUInt trueLabel, pUInt falseLabel): stmt(branchKind), + trueLabel_(trueLabel), falseLabel_(falseLabel), condition_(condition) {} + branch(expr* condition, label* trueLabel, label* falseLabel): stmt(branchKind), + trueLabel_(trueLabel->labelNo()), falseLabel_(falseLabel->labelNo()), + condition_(condition) {} + branch(pUInt destinationLabel): stmt(branchKind), + trueLabel_(destinationLabel), falseLabel_(-1), condition_(0) {} + branch(label* destinationLabel): stmt(branchKind), + trueLabel_(destinationLabel->labelNo()), falseLabel_(-1), condition_(0) {} + pUInt trueLabel() const { return trueLabel_; } + void setTrueLabel(pUInt n) { trueLabel_ = n; } + void setTrueLabel(label* n) { trueLabel_ = n->labelNo(); } + + pUInt falseLabel() const { return falseLabel_; } + void setFalseLabel(pUInt n) { falseLabel_ = n; } + void setFalseLabel(label* n) { falseLabel_ = n->labelNo(); } + + expr* condition() const { return condition_; } + void setCondition(expr *n) {condition_ = n; } + + stmt::child_iterator child_begin() { return reinterpret_cast(&condition_); } + stmt::child_iterator child_end() { return reinterpret_cast(&condition_+1); } + + IMPLEMENT_SUPPORT_MEMBERS(branch); +}; + +// This needs to be after the class label. +// function declaration +class functionDecl: public decl { + + enum { SIG, BODY, END_EXPR }; + stmt* children_[END_EXPR]; + // This contains the id the next label will recieve, so a function has labelCount_-1 labels. + pUInt labelCount_; + +protected: + functionDecl(const functionDecl& other, pParseContext& C): decl(other), + labelCount_(other.labelCount_) + { + memset(children_, 0, sizeof(children_)); + if(other.children_[SIG]) + children_[SIG] = other.children_[SIG]->clone(C); + if(other.children_[BODY]) + children_[BODY] = other.children_[BODY]->clone(C); + } + +public: + functionDecl(signature* sig, block* body): + decl(functionDeclKind), + children_(), labelCount_(0) + { + children_[SIG] = sig; + children_[BODY] = body; + } + + signature* sig(void) { return static_cast(children_[SIG]); } + block* body(void) { return static_cast(children_[BODY]); } + + stmt::child_iterator child_begin() { return (stmt**)&children_[0]; } + stmt::child_iterator child_end() { return (stmt**)&children_[0]+END_EXPR; } + + label* getNewLabel(pParseContext& C) { return new (C) label(labelCount_++); } + + IMPLEMENT_SUPPORT_MEMBERS(functionDecl); + +}; + + } } // namespace #endif /* RPHP_PAST_H_ */ diff --git a/include/rphp/analysis/passes/DumpAST.h b/include/rphp/analysis/passes/DumpAST.h index c98fb60..510fd1b 100644 --- a/include/rphp/analysis/passes/DumpAST.h +++ b/include/rphp/analysis/passes/DumpAST.h @@ -76,6 +76,8 @@ class DumpAST: public pBaseVisitor { void visit_pre_literalBool(literalBool* n); void visit_pre_literalArray(literalArray* n); void visit_pre_literalConstant(literalConstant* n); + void visit_pre_label(label* n); + void visit_pre_branch(branch* n); }; diff --git a/include/rphp/analysis/passes/Lower_Control_Flow.h b/include/rphp/analysis/passes/Lower_Control_Flow.h new file mode 100644 index 0000000..b2569aa --- /dev/null +++ b/include/rphp/analysis/passes/Lower_Control_Flow.h @@ -0,0 +1,62 @@ +/* ***** BEGIN LICENSE BLOCK ***** +;; Roadsend PHP Compiler +;; +;; Copyright (c) 2010 Cornelius Riemenschneider +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + ***** END LICENSE BLOCK ***** +*/ + +#ifndef RPHP_LOWERCONTROLFLOW_H +#define RPHP_LOWERCONTROLFLOW_H + +#include "rphp/analysis/pBaseTransformer.h" + +namespace rphp { namespace AST { namespace Pass { + +class Lower_Control_Flow: public pBaseTransformer { + + functionDecl* currentFunction_; + std::vector breakLabels_; + std::vector continueLabels_; + +public: + Lower_Control_Flow(pSourceModule *m): + pBaseTransformer("Lower_Control_Flow","Lowers the control flow.", m), + currentFunction_(0) + { + } + + stmt* transform_post_ifStmt(ifStmt* n); + + stmt* transform_pre_whileStmt(whileStmt* n); + stmt* transform_post_whileStmt(whileStmt* n); + + stmt* transform_post_breakStmt(breakStmt* n); + stmt* transform_post_continueStmt(continueStmt* n); + + stmt* transform_pre_forEach(forEach* n); + + + stmt* transform_pre_functionDecl(functionDecl* n); + +private: + stmt* lower_exit(std::vector& labels, expr* depthExpr); + +}; + +} } } // namespace + +#endif /* RPHP_LOWERCONTROLFLOW_H */ diff --git a/include/rphp/analysis/passes/MainFunction.h b/include/rphp/analysis/passes/MainFunction.h new file mode 100644 index 0000000..aafd380 --- /dev/null +++ b/include/rphp/analysis/passes/MainFunction.h @@ -0,0 +1,45 @@ +/* ***** BEGIN LICENSE BLOCK ***** +;; Roadsend PHP Compiler +;; +;; Copyright (c) 2010 Cornelius Riemenschneider +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + ***** END LICENSE BLOCK ***** +*/ + +#ifndef RPHP_MAINFUNCTION_H_ +#define RPHP_MAINFUNCTION_H_ + +#include "rphp/analysis/pBaseTransformer.h" + +namespace rphp { namespace AST { namespace Pass { + +class MainFunction: public pBaseTransformer { + bool firstBlock; + +public: + MainFunction(pSourceModule *m): + pBaseTransformer("MainFunction","Moves all global code in a __MAIN__ function", m), + firstBlock(false) + { + } + + stmt* transform_pre_block(block* n); + +}; + +} } } // namespace + +#endif /* RPHP_MAINFUNCTION_H_ */ diff --git a/include/rphp/runtime/pSupport.h b/include/rphp/runtime/pSupport.h index 8891074..32402ab 100644 --- a/include/rphp/runtime/pSupport.h +++ b/include/rphp/runtime/pSupport.h @@ -41,6 +41,8 @@ pUString toLowerCopy(const pUString& v); // this will allocate using new[], and returns the allocated buffer which // the caller is responsible for deleteing char* convertCodepage(const pUString& s, const char* codepage); +pBigInt convertStringLiteralToBigInt(pSourceString& s); + } diff --git a/runtime/pSupport.cpp b/runtime/pSupport.cpp index 139a2bc..e8fa54f 100644 --- a/runtime/pSupport.cpp +++ b/runtime/pSupport.cpp @@ -61,5 +61,28 @@ char* convertCodepage(const pUString& s, const char* codepage) { } +/** + * Edge cases: when we parse a literal float in a string (aka contains an [eE.]) + * we just stop parsing the integer there, as php does. Especially exponents like 1e5 are + * parsed as 1, not 10000! + */ +pBigInt convertStringLiteralToBigInt(pSourceRange& s) { + pSourceRange::const_iterator pos = s.begin(); + pSourceRange::const_iterator numEndPos = s.begin(); + while(pos != s.end()) { + if(*pos == '-' || (*pos >= '0' && *pos <= '9')) { + numEndPos = pos; + } + ++pos; + } + // normal strings evaluate to 0. + if(s.begin() == numEndPos) + return pBigInt(0); + pSourceString stringNum(s.begin(), numEndPos); + // This should never throw because we made a valid number out of that string. + return pBigInt(stringNum, 10); +} + + }