org.jruby.ext.ripper.RipperParserBase Maven / Gradle / Ivy
/*
***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2013-2017 The JRuby Team ([email protected])
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.ripper;
import java.io.IOException;
import org.jcodings.Encoding;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyString;
import org.jruby.lexer.LexerSource;
import org.jruby.runtime.Helpers;
import org.jruby.lexer.yacc.StackState;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
/**
*
*/
public class RipperParserBase {
public RipperParserBase(ThreadContext context, IRubyObject ripper, LexerSource source) {
this.context = context;
this.ripper = ripper;
this.lexer = new RipperLexer(this, source);
}
static int associateEncoding(ByteList buffer, Encoding ASCII8BIT_ENCODING, int codeRange) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public void reset() {
// inSingleton = 0;
// inDefinition = false;
}
public Object yyparse (RipperLexer yyLex) throws java.io.IOException {
return null;
}
public Object yyparse (RipperLexer yyLex, Object debugger) throws java.io.IOException {
return null;
}
/** The parse method use an lexer stream and parse it to an AST node
* structure
*/
public IRubyObject parse(boolean isDebug) throws IOException {
reset();
lexer.parser_prepare();
return (IRubyObject) yyparse(lexer, null);
}
public IRubyObject arg_add_optblock(IRubyObject arg1, IRubyObject arg2) {
// This has to be an MRI bug
if (arg2 == null) return dispatch("on_args_add_block", arg1, getRuntime().getFalse());
if (arg2.isNil()) return arg1;
return dispatch("on_args_add_block", arg1, arg2);
}
public IRubyObject arg_var(IRubyObject identifier) {
String name = lexer.getIdent();
StaticScope current = getCurrentScope();
// Multiple _ arguments are allowed. To not screw with tons of arity
// issues in our runtime we will allocate unnamed bogus vars so things
// still work. MRI does not use name as intern'd value so they don't
// have this issue.
if (name == "_") {
int count = 0;
while (current.exists(name) >= 0) {
name = "_$" + count++;
}
}
current.addVariableThisScope(name);
return identifier;
}
public IRubyObject assignable(IRubyObject name) {
// MRI calls get_id(name), but we differ quite a bit and we only need to worry about strings
// for anything we care about. Possibly this should return nil and not original value?
if (!(name instanceof RubyString)) return name;
String javaName = name.asJavaString();
if (javaName.equals("self")) {
yyerror("Can't change the value of self");
} else if (javaName.equals("nil")) {
yyerror("Can't assign to nil");
} else if (javaName.equals("true")) {
yyerror("Can't assign to true");
} else if (javaName.equals("false")) {
yyerror("Can't assign to false");
} else if (javaName.equals("__FILE__")) {
yyerror("Can't assign to __FILE__");
} else if (javaName.equals("__LINE__")) {
yyerror("Can't assign to __LINE__");
} else if (Character.isUpperCase(javaName.charAt(0))) { // MRI: ID_CONST
if (isInDef() || isInSingle()) dispatch("on_assign_error", name);
return name;
} else if (javaName.charAt(0) == '@') { // MRI: ID_CLASS & ID_INSTANCE
if (javaName.charAt(1) == '@') {
return name;
} else {
return name;
}
} else if (javaName.charAt(0) == '$') { // MRI: ID_GLOBAL
return name;
}
currentScope.assign(lexer.getPosition(), javaName.intern(), null);
return name;
}
public IRubyObject dispatch(String method_name) {
return Helpers.invoke(context, ripper, method_name);
}
public IRubyObject dispatch(String method_name, IRubyObject arg1) {
return Helpers.invoke(context, ripper, method_name, escape(arg1));
}
public IRubyObject dispatch(String method_name, IRubyObject arg1, IRubyObject arg2) {
return Helpers.invoke(context, ripper, method_name, escape(arg1), escape(arg2));
}
public IRubyObject dispatch(String method_name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
return Helpers.invoke(context, ripper, method_name, escape(arg1), escape(arg2), escape(arg3));
}
public IRubyObject dispatch(String method_name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, IRubyObject arg4) {
return Helpers.invoke(context, ripper, method_name, escape(arg1), escape(arg2), escape(arg3), escape(arg4));
}
public IRubyObject dispatch(String method_name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, IRubyObject arg4, IRubyObject arg5) {
return Helpers.invoke(context, ripper, method_name, escape(arg1), escape(arg2), escape(arg3), escape(arg4), escape(arg5));
}
public IRubyObject dispatch(String method_name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, IRubyObject arg4, IRubyObject arg5, IRubyObject arg6, IRubyObject arg7) {
return Helpers.invoke(context, ripper, method_name, escape(arg1), escape(arg2), escape(arg3), escape(arg4), escape(arg5), escape(arg6), escape(arg7));
}
public IRubyObject escape(IRubyObject arg) {
return arg == null ? context.runtime.getNil() : arg;
}
public IRubyObject formal_argument(IRubyObject identifier) {
return shadowing_lvar(identifier);
}
protected void getterIdentifierError(String identifier) {
throw new SyntaxException("identifier " + identifier + " is not valid", identifier);
}
// FIXME: Consider removing identifier.
public boolean is_id_var(IRubyObject identifier) {
String ident = lexer.getIdent().intern();
char c = ident.charAt(0);
if (c == '$' || c == '@' || Character.toUpperCase(c) == c) return true;
return getCurrentScope().getLocalScope().isDefined(ident) >= 0;
}
public boolean is_local_id(String identifier) {
return lexer.isIdentifierChar(identifier.charAt(0));
}
public IRubyObject intern(String value) {
return context.runtime.newSymbol(value);
}
public IRubyObject method_optarg(IRubyObject method, IRubyObject arg) {
if (arg == null) return method;
return dispatch("on_method_add_arg", method, arg);
}
public IRubyObject keyword_arg(IRubyObject key, IRubyObject value) {
RubyArray array = RubyArray.newArray(context.runtime, 2);
array.append(key);
if (value != null) {
array.append(value);
} else {
array.append(context.nil);
}
return array;
}
public IRubyObject new_args(IRubyObject f, IRubyObject o, IRubyObject r, IRubyObject p, ArgsTailHolder tail) {
if (tail != null) {
return dispatch("on_params", f, o, r, p, tail.getKeywordArgs(), tail.getKeywordRestArg(), tail.getBlockArg());
}
return dispatch("on_params", f, o, r, p, null, null, null);
}
public ArgsTailHolder new_args_tail(IRubyObject kwarg, IRubyObject kwargRest, IRubyObject block) {
return new ArgsTailHolder(kwarg, kwargRest, block);
}
public IRubyObject method_add_block(IRubyObject method, IRubyObject block) {
return dispatch("on_method_add_block", method, block);
}
public IRubyObject internalId() {
return null;
}
public IRubyObject new_array(IRubyObject arg) {
return context.runtime.newArray(arg);
}
public IRubyObject new_assoc(IRubyObject key, IRubyObject value) {
return RubyArray.newArray(context.runtime, key, value);
}
public IRubyObject new_bv(IRubyObject identifier) {
String ident = lexer.getIdent();
if (!is_local_id(ident)) getterIdentifierError(ident);
return arg_var(shadowing_lvar(identifier));
}
public void popCurrentScope() {
currentScope = currentScope.getEnclosingScope();
}
public void pushBlockScope() {
currentScope = getRuntime().getStaticScopeFactory().newBlockScope(currentScope);
}
public void pushLocalScope() {
currentScope = getRuntime().getStaticScopeFactory().newLocalScope(currentScope);
}
public int getHeredocIndent() {
return lexer.getHeredocIndent();
}
public void setHeredocIndent(int indent) {
lexer.setHeredocIndent(indent);
}
public void heredoc_dedent(IRubyObject array) {
if (lexer.getHeredocIndent() <= 0) return;
dispatch("on_heredoc_dedent", array, getRuntime().newFixnum(lexer.getHeredocIndent()));
}
public void setCommandStart(boolean value) {
lexer.commandStart = value;
}
public IRubyObject shadowing_lvar(IRubyObject identifier) {
String name = lexer.getIdent();
if (name == "_") return identifier;
StaticScope current = getCurrentScope();
if (current.isBlockScope()) {
if (current.exists(name) >= 0) yyerror("duplicated argument name");
if (current.isDefined(name) >= 0) {
lexer.warning("shadowing outer local variable - %s", name);
}
} else if (current.exists(name) >= 0) {
yyerror("duplicated argument name");
}
return identifier;
}
public StackState getConditionState() {
return lexer.getConditionState();
}
public boolean isInDef() {
return inDefinition;
}
public boolean isInSingle() {
return inSingleton != 0;
}
public StrTerm getStrTerm() {
return lexer.getStrTerm();
}
public void setStrTerm(StrTerm object) {
lexer.setStrTerm(object);
}
public StackState getCmdArgumentState() {
return lexer.getCmdArgumentState();
}
public void compile_error(String message) {
System.out.println(getRuntime().newString(message));
dispatch("on_parse_error", getRuntime().newString(message));
}
public void yyerror(String message) {
compile_error(message);
error();
throw new SyntaxException(message, message);
}
public void yyerror(String message, String[] expected, String found) {
error();
compile_error(message + ", unexpected " + found + "\n");
}
public void error() {
this.isError = true;
}
public Integer getLeftParenBegin() {
return lexer.getLeftParenBegin();
}
public void setLeftParenBegin(Integer integer) {
lexer.setLeftParenBegin(integer);
}
public void setInDef(boolean inDefinition) {
this.inDefinition = inDefinition;
}
public void setInSingle(int inSingleton) {
this.inSingleton = inSingleton;
}
public int getInSingle() {
return inSingleton;
}
public int getBraceNest() {
return lexer.getBraceNest();
}
public int getState() {
return lexer.getState();
}
public void setBraceNest(int braceNest) {
lexer.setBraceNest(braceNest);
}
public void setState(int lexState) {
lexer.setState(lexState);
}
public void warning(String message) {
if (lexer.isVerbose()) lexer.warning(message);
}
public void warn(String message) {
lexer.warn(message);
}
public Integer incrementParenNest() {
return lexer.incrementParenNest();
}
public StaticScope getCurrentScope() {
return currentScope;
}
public Ruby getRuntime() {
return context.runtime;
}
public long getColumn() {
return lexer.column();
}
public long getLineno() {
return lexer.lineno();
}
public boolean hasStarted() {
return lexer.hasStarted();
}
public Encoding encoding() {
return lexer.getEncoding();
}
public boolean getYYDebug() {
return yydebug;
}
public void setYYDebug(boolean yydebug) {
this.yydebug = yydebug;
}
public boolean isEndSeen() {
return lexer.isEndSeen();
}
public boolean isError() {
return isError;
}
public ThreadContext getContext() {
return context;
}
protected IRubyObject ripper;
protected ThreadContext context;
protected RipperLexer lexer;
protected StaticScope currentScope;
protected boolean inDefinition;
protected boolean yydebug; // FIXME: Hook up to yydebug
protected boolean isError;
// Is the parser current within a singleton (value is number of nested singletons)
protected int inSingleton;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy