diff options
14 files changed, 258 insertions, 347 deletions
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java index a93b230f..b11d1ddb 100644 --- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java +++ b/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java @@ -1,48 +1,94 @@ +/* + * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * <p> + * Contributor(s): + * <p> + * The Initial Developer of the Original Software is Dibyendu Majumdar. + * <p> + * This file is part of the Sea Of Nodes Simple Programming language + * implementation. See https://github.com/SeaOfNodes/Simple + * <p> + * The contents of this file are subject to the terms of the + * Apache License Version 2 (the "APL"). You may not use this + * file except in compliance with the License. A copy of the + * APL may be obtained from: + * http://www.apache.org/licenses/LICENSE-2.0 + */ package com.seaofnodes.simple.parser; -import com.seaofnodes.simple.common.CompilerException; import com.seaofnodes.simple.lexer.ecstasy.Token; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; -public abstract class Ast { +/** + * A simple AST definition. + */ +public class Ast { - public static class Node { + private Ast() { } - public static class Expr extends Node { + public interface Node { + } + + public abstract static class Expr implements Node { public final Token op; - public Expr(Token op) { + protected Expr(Token op) { this.op = op; } } + /** + * A name in the language + */ public static class Identifier extends Expr { public final String name; + public Identifier(Token op) { super(op); name = op.getValueText(); } + + @Override + public String toString() { + return name; + } } + /** + * Reference to a name in the language + */ public static class Symbol extends Expr { public final String name; + public Symbol(Token op) { super(op); name = op.getValueText(); } + + @Override + public String toString() { + return name; + } } public static class Binary extends Expr { - public final Expr expr1, expr2; + public final Expr expr1; + public final Expr expr2; public Binary(Token op, Expr expr1, Expr expr2) { super(op); this.expr1 = expr1; this.expr2 = expr2; } + + @Override + public String toString() { + return "(" + expr1 + op.getValueText() + expr2 + ")"; + } } public static class Unary extends Expr { @@ -52,49 +98,65 @@ public abstract class Ast { super(op); this.expr = expr; } + + @Override + public String toString() { + return "(" + op.getValueText() + "(" + expr + "))"; + } } public static class Constant extends Expr { public Constant(Token op) { super(op); } + + @Override + public String toString() { + return op.getValueText(); + } } - public static class Statement extends Node { + public abstract static class Statement implements Node { } public static class IfElse extends Statement { public final Expr expr; - public final Statement ifStmt, elseStmt; + public final Statement ifStmt; + public final Statement elseStmt; public IfElse(Expr expr, Statement ifStmt, Statement elseStmt) { this.expr = expr; this.ifStmt = ifStmt; this.elseStmt = elseStmt; } + + @Override + public String toString() { + if (elseStmt != null) + return "if(" + expr + ") " + ifStmt + " else " + elseStmt; + return "if(" + expr + ") " + ifStmt; + } } public static class While extends Statement { - public Expr expr; - public Statement stmt; + public final Expr expr; + public final Statement stmt; - public While() { - } - public void init(Expr expr, Statement stmt) { + public While(Expr expr, Statement stmt) { this.expr = expr; this.stmt = stmt; } + + @Override + public String toString() { + return "while(" + expr.toString() + ") " + stmt; + } } public static class Break extends Statement { - public final Statement stmt; - - public Break(Statement stmt) { - if (stmt == null) - { - throw new CompilerException("uneclosed break"); - } - this.stmt = stmt; + @Override + public String toString() { + return "break;"; } } @@ -106,14 +168,24 @@ public abstract class Ast { this.identifier = identifier; this.expr = expr; } + + @Override + public String toString() { + return identifier.toString() + "=" + expr.toString() + ";"; + } } - public static class Type extends Node { + public static class Type implements Node { public final Token.Id typeId; public Type(Token.Id typeId) { this.typeId = typeId; } + + @Override + public String toString() { + return typeId.TEXT; + } } public static class Declare extends Statement { @@ -124,9 +196,20 @@ public abstract class Ast { this.type = type; this.identifier = identifier; } + + @Override + public String toString() { + return type.toString() + " " + identifier.toString() + ";"; + } } public static class Block extends Statement { public final List<Statement> stmtList = new ArrayList<>(); + + @Override + public String toString() { + return "{" + + stmtList.stream().map(Object::toString).collect(Collectors.joining()) + "}"; + } } } diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java index b36942d4..263d0b41 100644 --- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java +++ b/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java @@ -1,4 +1,4 @@ -/** +/* * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Contributor(s): @@ -16,28 +16,28 @@ */ package com.seaofnodes.simple.parser; -import com.seaofnodes.simple.common.CompilerException; +import com.seaofnodes.simple.lexer.ecstasy.CompilerException; import com.seaofnodes.simple.lexer.ecstasy.Lexer; import com.seaofnodes.simple.lexer.ecstasy.Token; -import com.seaofnodes.simple.common.Severity; +import com.seaofnodes.simple.lexer.ecstasy.Severity; public class Parser { final Lexer lexer; Token look; - Ast.Statement statementEnclosing; public Parser(Lexer lexer) { this.lexer = lexer; } - Token nextOrEof() { + private Token nextOrEof() { if (lexer.hasNext()) return lexer.next(); return new Token(look.getStartPosition(), look.getEndPosition(), Token.Id.EOF); } - void nextToken() { + private void nextToken() { look = nextOrEof(); + // skip comments while (look.isComment()) { look = nextOrEof(); } @@ -47,12 +47,12 @@ public class Parser { } } - void error(Token t, String errorMessage) { + private void error(Token t, String errorMessage) { lexer.log(Severity.ERROR, errorMessage, t.getStartPosition(), t.getEndPosition()); throw new CompilerException(errorMessage + ": " + t.toDebugString()); } - void match(Token.Id tag) { + private void match(Token.Id tag) { if (look.getId() == tag) { nextToken(); } else { @@ -65,43 +65,40 @@ public class Parser { return parseProgram(); } - Ast.Statement parseProgram() { + private Ast.Statement parseProgram() { var block = new Ast.Block(); parseDeclarations(block); parseStatements(block); return block; } - void parseDeclarations(Ast.Block block) { - while (look.getId() == Token.Id.VAR) { + private boolean isTypeName(Token tok) { + return switch (tok.getId()) { + case TYPE_INT, TYPE_CHAR, TYPE_FLOAT -> true; + default -> false; + }; + } + + private void parseDeclarations(Ast.Block block) { + while (isTypeName(look)) { + var type = new Ast.Type(look.getId()); nextToken(); var tok = look; match(Token.Id.IDENTIFIER); - match(Token.Id.COLON); - var type = parseType(); match(Token.Id.SEMICOLON); var identifier = new Ast.Identifier(tok); block.stmtList.add(new Ast.Declare(type, identifier)); } } - Ast.Type parseType() { - if (look.getId() == Token.Id.LIT_INTA) { - nextToken(); - return new Ast.Type(look.getId()); - } - error(look, "syntax error: unsupported type (at present only Int type is allowed)"); - return null; - } - - void parseStatements(Ast.Block block) { + private void parseStatements(Ast.Block block) { while (look.getId() != Token.Id.EOF && look.getId() != Token.Id.R_CURLY) { block.stmtList.add(parseStatement()); } } - Ast.Statement parseStatement() { + private Ast.Statement parseStatement() { Ast.Expr x; Ast.Statement s1; Ast.Statement s2; @@ -120,22 +117,17 @@ public class Parser { return new Ast.IfElse(x, s1, s2); } case WHILE -> { - var whilenode = new Ast.While(); - var savedStmt = statementEnclosing; - statementEnclosing = whilenode; match(Token.Id.WHILE); match(Token.Id.L_PAREN); x = parseBool(); match(Token.Id.R_PAREN); s1 = parseStatement(); - whilenode.init(x, s1); - statementEnclosing = savedStmt; - return whilenode; + return new Ast.While(x, s1); } case BREAK -> { match(Token.Id.BREAK); match(Token.Id.SEMICOLON); - return new Ast.Break(statementEnclosing); + return new Ast.Break(); } case L_CURLY -> { return parseBlock(); @@ -146,7 +138,7 @@ public class Parser { } } - Ast.Statement parseBlock() { + private Ast.Statement parseBlock() { match(Token.Id.L_CURLY); var block = new Ast.Block(); parseDeclarations(block); @@ -155,16 +147,17 @@ public class Parser { return block; } - Ast.Statement parseAssign() { + private Ast.Statement parseAssign() { + Token tok = look; match(Token.Id.IDENTIFIER); - Ast.Identifier identifier = new Ast.Identifier(look); + Ast.Identifier identifier = new Ast.Identifier(tok); match(Token.Id.ASN); var s = new Ast.Assign(identifier, parseBool()); match(Token.Id.SEMICOLON); return s; } - Ast.Expr parseBool() { + private Ast.Expr parseBool() { var x = parseAnd(); while (look.getId() == Token.Id.COND_OR) { var tok = look; @@ -174,7 +167,7 @@ public class Parser { return x; } - Ast.Expr parseAnd() { + private Ast.Expr parseAnd() { var x = parseRelational(); while (look.getId() == Token.Id.COND_AND) { var tok = look; @@ -184,7 +177,7 @@ public class Parser { return x; } - Ast.Expr parseRelational() { + private Ast.Expr parseRelational() { var x = parseAddition(); while (look.getId() == Token.Id.COMP_EQ || look.getId() == Token.Id.COMP_NEQ || @@ -199,7 +192,7 @@ public class Parser { return x; } - Ast.Expr parseAddition() { + private Ast.Expr parseAddition() { var x = parseMultiplication(); while (look.getId() == Token.Id.ADD || look.getId() == Token.Id.SUB) { @@ -210,7 +203,7 @@ public class Parser { return x; } - Ast.Expr parseMultiplication() { + private Ast.Expr parseMultiplication() { var x = parseUnary(); while (look.getId() == Token.Id.MUL || look.getId() == Token.Id.DIV) { @@ -221,7 +214,7 @@ public class Parser { return x; } - Ast.Expr parseUnary() { + private Ast.Expr parseUnary() { if (look.getId() == Token.Id.SUB || look.getId() == Token.Id.NOT) { var tok = look; @@ -232,7 +225,7 @@ public class Parser { } } - Ast.Expr parsePrimary() { + private Ast.Expr parsePrimary() { switch (look.getId()) { case L_PAREN -> { nextToken(); diff --git a/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java b/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java index a89c1f6a..80917c0f 100644 --- a/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java +++ b/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java @@ -1,4 +1,4 @@ -/** +/* * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Contributor(s): @@ -15,10 +15,9 @@ package com.seaofnodes.simple.lexer; -import com.seaofnodes.simple.common.ErrorListener; +import com.seaofnodes.simple.lexer.ecstasy.ErrorListener; import com.seaofnodes.simple.lexer.ecstasy.Lexer; import com.seaofnodes.simple.lexer.ecstasy.Source; -import com.seaofnodes.simple.parser.Ast; import com.seaofnodes.simple.parser.Parser; import org.junit.Assert; import org.junit.Test; @@ -28,30 +27,28 @@ public class TestParser { @Test public void testParseSingleDecl() { - final String source = "var i: Int;"; + final String source = "int i;"; Lexer lexer = new Lexer(new Source(source), new ErrorListener()); Parser parser = new Parser(lexer); var ast = parser.parse(); - Assert.assertTrue(ast instanceof Ast.Block - && ((Ast.Block) ast).stmtList.size() == 1); + Assert.assertEquals("{int i;}", ast.toString()); } @Test public void testParseTwoDecls() { - final String source = "var i: Int;\nvar j: Int;"; + final String source = "int i;\nint j;"; Lexer lexer = new Lexer(new Source(source), new ErrorListener()); Parser parser = new Parser(lexer); var ast = parser.parse(); - Assert.assertTrue(ast instanceof Ast.Block - && ((Ast.Block) ast).stmtList.size() == 2); + Assert.assertEquals("{int i;int j;}", ast.toString()); } @Test public void testIfAndWhile() { final String source = """ - var i: Int; - var j: Int; + int i; + int j; i = 0; j = 100; @@ -64,5 +61,6 @@ public class TestParser { Lexer lexer = new Lexer(new Source(source), new ErrorListener()); Parser parser = new Parser(lexer); var ast = parser.parse(); + Assert.assertEquals("{int i;int j;i=0;j=100;while(1) {i=(i+1);j=(j-1);if((i>=j)) break;}}", ast.toString()); } } diff --git a/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java b/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java deleted file mode 100644 index 518dcde6..00000000 --- a/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.common; - -public class CompilerException extends RuntimeException { - public CompilerException(String message) { - super(message); - } - - public CompilerException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/common/src/main/java/com/seaofnodes/simple/common/Severity.java b/common/src/main/java/com/seaofnodes/simple/common/Severity.java deleted file mode 100644 index f3325d52..00000000 --- a/common/src/main/java/com/seaofnodes/simple/common/Severity.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.common; - - -/** - * Severity levels. - */ -public enum Severity - { - NONE, INFO, WARNING, ERROR, FATAL; - - public String desc() - { - String sName = name(); - StringBuilder sb = new StringBuilder(sName.length()); - sb.append(sName.charAt(0)); - sb.append(sName.substring(1).toLowerCase()); - return sb.toString(); - } - } diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java b/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java deleted file mode 100644 index 162cdea1..00000000 --- a/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java +++ /dev/null @@ -1,228 +0,0 @@ -/** - * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Contributor(s): - * - * The Initial Developer of the Original Software is Cliff Click. - * - * This file is part of the Sea Of Nodes Simple Programming language - * implementation. See https://github.com/SeaOfNodes/Simple - * - * The contents of this file are subject to the terms of the - * Apache License Version 2 (the "APL"). You may not use this - * file except in compliance with the License. A copy of the - * APL may be obtained from: - * http://www.apache.org/licenses/LICENSE-2.0 - */ -package com.seaofnodes.simple.lexer.aa; - -import com.seaofnodes.util.AryInt; -import com.seaofnodes.util.SB; - -import java.text.NumberFormat; -import java.text.ParsePosition; - -public class Lexer { - private final String _srcName; // Source for error messages; usually a file name - private final byte[] _buf; - private int _x; // Parser index - private int _lastNWS; // Index of last non-white-space char - private final AryInt _lines; // char offset of each line - - private final NumberFormat _nf; - private final ParsePosition _pp; - private final String _str; - - public Lexer(String srcName, String source) - { - _srcName = srcName; - _buf = source.getBytes(); - _x = 0; - - // Set fields strictly for Java number parsing - _nf = NumberFormat.getInstance(); - _nf.setGroupingUsed(false); - _pp = new ParsePosition(0); - _str = source; // Keep a complete string copy for java number parsing - _lines = new AryInt();// - _lines.push(0); // Line 0 at offset 0 - } - - // Skip WS, return true&skip if match, false & do not skip if miss. - public boolean peek( char c ) { return peek1(skipWS(),c); } - public boolean peek_noWS( char c ) { return peek1(_x >= _buf.length ? -1 : _buf[_x],c); } - // Already skipped WS & have character; - // return true & skip if a match, false& do not skip if a miss. - public boolean peek1( byte c0, char c ) { - assert c0==-1 || c0== _buf[_x]; - if( c0!=c ) return false; - _x++; // Skip peeked character - return true; - } - // Already skipped WS & have character; - // return true&skip if match, false & do not skip if miss. - public boolean peek2( byte c0, String s2 ) { - if( c0 != s2.charAt(0) ) return false; - if( _x+1 >= _buf.length || _buf[_x+1] != s2.charAt(1) ) return false; - _x+=2; // Skip peeked characters - return true; - } - public boolean peek( String s ) { - if( !peek(s.charAt(0)) ) return false; - if( !peek_noWS(s.charAt(1)) ) { _x--; return false; } - return true; - } - // Peek 'c' and NOT followed by 'no' - public boolean peek_not( char c, char no ) { - byte c0 = skipWS(); - if( c0 != c || (_x+1 < _buf.length && _buf[_x+1] == no) ) return false; - _x++; - return true; - } - // Match any of these, and return the match or null - public String peek(String[] toks) { - for( String tok : toks ) if( peek1(tok) ) return tok; - return null; - } - public boolean peek1(String tok) { - for( int i=0; i<tok.length(); i++ ) - if( _x+i >= _buf.length || _buf[_x+i] != tok.charAt(i) ) - return false; - return true; - } - - - /** Advance parse pointer to the first non-whitespace character, and return - * that character, -1 otherwise. */ - public byte skipWS() { - int oldx = _x; - while( _x < _buf.length ) { - byte c = _buf[_x]; - if( c=='/' && _x+1 < _buf.length && _buf[_x+1]=='/' ) { skipEOL() ; continue; } - if( c=='/' && _x+1 < _buf.length && _buf[_x+1]=='*' ) { skipBlock(); continue; } - if( c=='\n' && _x+1 > _lines.last() ) _lines.push(_x+1); - if( !isWS(c) ) { - if( oldx-1 > _lastNWS && !isWS(_buf[oldx-1]) ) _lastNWS = oldx-1; - return c; - } - _x++; - } - return -1; - } - public void skipEOL () { while( _x < _buf.length && _buf[_x] != '\n' ) _x++; } - public void skipBlock() { throw new UnsupportedOperationException("Block comments not yet implemented"); } - // Advance parse pointer to next WS. Used for parser syntax error recovery. - public void skipNonWS() { - while( _x < _buf.length && !isWS(_buf[_x]) ) _x++; - } - - /** Return true if `c` passes a test */ - public static boolean isWS (byte c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } - public static boolean isAlpha0(byte c) { return ('a'<=c && c <= 'z') || ('A'<=c && c <= 'Z') || (c=='_'); } - public static boolean isAlpha1(byte c) { return isAlpha0(c) || ('0'<=c && c <= '9'); } - public static boolean isJava (byte c) { return isAlpha1(c) || (c=='$') || (c=='.'); } - public static boolean isDigit (byte c) { return '0' <= c && c <= '9'; } - - public String token() { skipWS(); return token0(); } - // Lexical tokens. Any alpha, followed by any alphanumerics is a alpha- - // token; alpha-tokens end with WS or any operator character. Any collection - // of the classic operator characters are a token, except that they will break - // un-ambiguously. - public String token0() { - if( _x >= _buf.length ) return null; - byte c=_buf[_x]; int x = _x; - if( isOp0(c) || (c=='_' && _x+1 < _buf.length && isOp0(_buf[_x+1])) ) - while( _x < _buf.length && isOp1(_buf[_x]) ) _x++; - else if( isAlpha0(c) ) - while( _x < _buf.length && isAlpha1(_buf[_x]) ) _x++; - else return null; // Not a token; specifically excludes e.g. all bytes >= 128, or most bytes < 32 - if( (c==':' || c==',') && _x-x==1 ) // Disallow bare ':' as a token; ambiguous with ?: and type annotations; same for ',' - { _x=x; return null; } // Unwind, not a token - if( c=='-' && _x-x>2 && _buf[x+1]=='>' ) // Disallow leading "->", confusing with function parameter list end; eg "not={x->!x}" - _x=x+2; // Just return the "->" - return new String(_buf,x,_x-x); - } - public boolean isOp(String s) { - if( s==null || s.isEmpty() ) return false; - byte c = (byte)s.charAt(0); - if( !isOp0(c) && (c!='_' || !isOp0((byte)s.charAt(1))) ) return false; - for( int i=1; i<s.length(); i++ ) - if( !isOp1((byte)s.charAt(i)) ) return false; - return true; - } - - // Parse a number; WS already skipped and sitting at a digit. Relies on - // Javas number parsing. - public Number number() { - _pp.setIndex(_x); - Number n = _nf.parse(_str,_pp); - _x = _pp.getIndex(); - if( n instanceof Long ) { - if( _buf[_x-1]=='.' ) _x--; // Java default parser allows "1." as an integer; pushback the '.' - } - return n; - } - - public int field_number() { - byte c = _buf[_x]; - if( !isDigit(c) ) return -1; - _x++; - int sum = c-'0'; - while( _x < _buf.length && isDigit(c=_buf[_x]) ) { - _x++; - sum = sum*10+c-'0'; - } - return sum; - } - - /** Parse a String; _x is at '"'. - * str = [.\%]* // String contents; \t\n\r\% standard escapes - * str = %[num]?[.num]?fact // Percent escape embeds a 'fact' in a string; "name=%name\n" - */ - public String string() { - int oldx = ++_x; - byte c; - while( (c=_buf[_x++]) != '"' ) { - if( c=='%' ) throw new UnsupportedOperationException("Not implemented"); - if( c=='\\' ) throw new UnsupportedOperationException("Not implemented"); - if( _x == _buf.length ) return null; - } - return new String(_buf,oldx,_x-oldx-1).intern(); - } - - public static boolean isOp0(byte c) { return "!#$%*+-=<>^[]~/&|".indexOf(c) != -1; } - public static boolean isOp1(byte c) { return isOp0(c) || ":?_{}".indexOf(c) != -1; } - - public String errLocMsg(String s) { - if( s.charAt(0)=='\n' ) return s; - // find line start - int a=_x; - while( a > 0 && _buf[a-1] != '\n' ) --a; - if( _buf[a]=='\r' ) a++; // do not include leading \n or \n\r - // find line end - int b=_x; - while( b < _buf.length && _buf[b] != '\n' ) b++; - if( b < _buf.length ) b--; // do not include trailing \n or \n\r - // Find line number. Bin-search returns the insertion-point, which is the NEXT - // line unless _x is exactly a line start. - int line = _lines.binary_search(_x); // Find zero-based line insertion point - if( line == _lines._len || _lines.at(line)>_x ) line--; - // error message using 1-based line - SB sb = new SB().p(_srcName).p(':').p(line+1).p(':').p(s).nl(); - sb.p(new String(_buf,a,b-a)).nl(); - int line_start = a; - for( int i=line_start; i<_x; i++ ) - sb.p(' '); - return sb.p('^').nl().toString(); - } - - @Override public String toString() { return new String(_buf,_x,_buf.length-_x); } - @Override public boolean equals(Object loc) { - if( this==loc ) return true; - if( !(loc instanceof Lexer p) ) return false; - return _x==p._x && _srcName.equals(p._srcName); - } - @Override public int hashCode() { - return _srcName.hashCode()+_x; - } -} diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java new file mode 100644 index 00000000..c23d3672 --- /dev/null +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java @@ -0,0 +1,28 @@ +/* + * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Contributor(s): + * + * The Initial Developer of the Original Software is + * The Ecstasy Project (https://github.com/xtclang/). + * + * This file is part of the Sea Of Nodes Simple Programming language + * implementation. See https://github.com/SeaOfNodes/Simple + * + * The contents of this file are subject to the terms of the + * Apache License Version 2 (the "APL"). You may not use this + * file except in compliance with the License. A copy of the + * APL may be obtained from: + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.seaofnodes.simple.lexer.ecstasy; + +public class CompilerException extends RuntimeException { + public CompilerException(String message) { + super(message); + } + + public CompilerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/src/main/java/com/seaofnodes/simple/common/ErrorListener.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/ErrorListener.java index 29ca46b6..6a29f315 100644 --- a/common/src/main/java/com/seaofnodes/simple/common/ErrorListener.java +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/ErrorListener.java @@ -1,11 +1,26 @@ -package com.seaofnodes.simple.common; +/* + * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Contributor(s): + * + * The Initial Developer of the Original Software is + * The Ecstasy Project (https://github.com/xtclang/). + * + * This file is part of the Sea Of Nodes Simple Programming language + * implementation. See https://github.com/SeaOfNodes/Simple + * + * The contents of this file are subject to the terms of the + * Apache License Version 2 (the "APL"). You may not use this + * file except in compliance with the License. A copy of the + * APL may be obtained from: + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.seaofnodes.simple.lexer.ecstasy; import java.text.MessageFormat; import java.util.Arrays; -import com.seaofnodes.simple.lexer.ecstasy.Source; - import static com.seaofnodes.util.Handy.quotedString; diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java index 90571ffd..86a7b77f 100644 --- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java @@ -1,4 +1,4 @@ -/** +/* * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Contributor(s): @@ -28,10 +28,8 @@ import java.util.NoSuchElementException; import java.util.function.Consumer; -import com.seaofnodes.simple.common.ErrorListener; import com.seaofnodes.simple.lexer.ecstasy.Token.Id; import com.seaofnodes.util.PackedInteger; -import com.seaofnodes.simple.common.Severity; import static com.seaofnodes.util.Handy.*; @@ -1076,6 +1074,17 @@ public class Lexer { switch (name) { + case "int": + idNum = Id.TYPE_INT; + break; + case "float": + idNum = Id.TYPE_FLOAT; + break; + case "char": + idNum = Id.TYPE_CHAR; + break; + + // Following are Ecstasy ids case "Bit": idNum = Id.LIT_BIT; break; diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java new file mode 100644 index 00000000..9b01a7e4 --- /dev/null +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java @@ -0,0 +1,36 @@ +/* + * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Contributor(s): + * + * The Initial Developer of the Original Software is + * The Ecstasy Project (https://github.com/xtclang/). + * + * This file is part of the Sea Of Nodes Simple Programming language + * implementation. See https://github.com/SeaOfNodes/Simple + * + * The contents of this file are subject to the terms of the + * Apache License Version 2 (the "APL"). You may not use this + * file except in compliance with the License. A copy of the + * APL may be obtained from: + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.seaofnodes.simple.lexer.ecstasy; + + +/** + * Severity levels. + */ +public enum Severity + { + NONE, INFO, WARNING, ERROR, FATAL; + + public String desc() + { + String sName = name(); + StringBuilder sb = new StringBuilder(sName.length()); + sb.append(sName.charAt(0)); + sb.append(sName.substring(1).toLowerCase()); + return sb.toString(); + } + } diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java index 6800138a..c0da5d5f 100644 --- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java @@ -1,4 +1,4 @@ -/** +/* * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Contributor(s): diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java index 271d8fbc..18cb5759 100644 --- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java +++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java @@ -1,4 +1,4 @@ -/** +/* * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Contributor(s): @@ -19,9 +19,6 @@ package com.seaofnodes.simple.lexer.ecstasy; -import com.seaofnodes.simple.common.ErrorListener; -import com.seaofnodes.simple.common.Severity; - import java.util.HashMap; import java.util.Map; @@ -432,6 +429,9 @@ public class Token case LIT_FLOAT128: case LIT_FLOATN: case LIT_BFLOAT16: + case TYPE_INT: + case TYPE_FLOAT: + case TYPE_CHAR: sb.append(m_id.TEXT) .append(':') .append(m_oValue); @@ -637,6 +637,13 @@ public class Token IDENTIFIER (null ), EOL_COMMENT (null ), ENC_COMMENT (null ), + + // Simple language type keywords + TYPE_INT ("int" , false, true), + TYPE_FLOAT ("float" , false, true), + TYPE_CHAR ("char" , false, true), + // End Simple language type keywords + LIT_BIT (null ), LIT_NIBBLE (null ), LIT_CHAR (null ), diff --git a/common/src/main/java/com/seaofnodes/util/Handy.java b/common/src/main/java/com/seaofnodes/util/Handy.java index f3496f8c..62e34ab9 100644 --- a/common/src/main/java/com/seaofnodes/util/Handy.java +++ b/common/src/main/java/com/seaofnodes/util/Handy.java @@ -518,7 +518,7 @@ public class Handy */ public static boolean isDigit(char ch) { - return ch >= '0' & ch <= '9'; + return ch >= '0' && ch <= '9'; } /** @@ -547,7 +547,7 @@ public class Handy // {..., 0, ..., 9, ..., A, ..., F, ..., a, ..., f, ...} // so a hexit has to be in the range 0..f, and the entire character // range from 0..f is only 0x36 (so it fits inside a long mask) - return ch >= '0' & ch <= 'f' & ((1L << ('f' - ch)) & + return ch >= '0' && ch <= 'f' && ((1L << ('f' - ch)) & // 3 4 5 6 // 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456 // 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdef @@ -579,7 +579,7 @@ public class Handy { // ASCII letters in the unicode range appear in this order: // {..., A, ..., Z, ..., a, ..., z, ...} - return ch >= 'A' & ch <= 'z' & ((1L << ('z' - ch)) & + return ch >= 'A' && ch <= 'z' && ((1L << ('z' - ch)) & // 4 5 6 7 // 123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789A // ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnopqrstuvwxyz diff --git a/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java b/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java index ef29d963..aec5d171 100644 --- a/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java +++ b/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java @@ -15,7 +15,7 @@ package com.seaofnodes.simple.lexer; -import com.seaofnodes.simple.common.ErrorListener; +import com.seaofnodes.simple.lexer.ecstasy.ErrorListener; import com.seaofnodes.simple.lexer.ecstasy.Lexer; import com.seaofnodes.simple.lexer.ecstasy.Source; import com.seaofnodes.simple.lexer.ecstasy.Token; |
